Skip to content

Domain

Domains are auxiliary structures that facilitate the management of EventHandlers, CommandHandlers, and QueryHandlers. They are optional and can be used to improve the application's structure. Here, we demonstrate best practices for creating Domains.

When we create a Domain, we start by creating a new folder. In this example, we name the domain simple. Inside this folder, we create the file domain/simple/domain.go. This file contains the Domain structure and the methods that the domain will use.

go
// domain/simple/domain.go
package simple

import (
	"my/app/domain/simple/command"
	"my/app/domain/simple/query"
	"my/app/domain/simple/readmodel"
	"github.com/gradientzero/es-cqrs-base/base"
)

type domain struct {
	eventHandlerList   []base.EventHandler
	commandHandlerList []base.CommandHandler
	queryHandlerList   []base.QueryHandler
}

// Make sure it implements interface
var _ base.Domain = (*domain)(nil)

type Options struct {
	AnyOptionalModule *AnyOptionalModule
}

type Option func(opt *Options) (*Options, error)

func OptionWithAnyOptionalModule(AnyOptionalModule *AnyOptionalModule) Option {
	return func(opt *Options) (*Options, error) {
		opt.AnyOptionalModule = AnyOptionalModule
		return opt, nil
	}
}

func NewDomain(
	EventRepository base.EventRepository,
	AggregateRepository base.AggregateRepository,
	opts ...Option,
) base.Domain {
	domainOpts := Options{}
	for _, opt := range opts {
		if _, err := opt(&domainOpts); err != nil {
			panic(err)
		}
	}
	readModelStore := readmodel.NewReadModelStoreMemory()
	readModel := readmodel.NewReadModel(EventRepository, readModelStore)

	pkg := &domain{}
	pkg.eventHandlerList = []base.EventHandler{
		readModel,
	}
	pkg.commandHandlerList = []base.CommandHandler{
		command.NewCommandHandler(AggregateRepository),
	}
	pkg.queryHandlerList = []base.QueryHandler{
		query.NewQueryHandler(readModel),
	}
	return pkg
}

func (pkg *domain) GetEventHandlerList() []base.EventHandler {
	return pkg.eventHandlerList
}

func (pkg *domain) GetCommandHandlerList() []base.CommandHandler {
	return pkg.commandHandlerList
}

func (pkg *domain) GetQueryHandlerList() []base.QueryHandler {
	return pkg.queryHandlerList
}

That's essentially what a domain is. It's a structure that holds the handlers for events, commands, and queries, fullfilling the interface Domain. The optional functions are used when something external needs to be passed to the domain, such as an authorization module or a module for webhooks.

Finally, the domain can be added to the Facade as follows:

go
// setup simple domain for facade
simpleDomainOpts := []simple.Option{}
if somehowTrue {
	simpleDomainOpts = append(simpleDomainOpts, simple.OptionWithAnyOptionalModule(anyOptionalModule))
}
simpleDomain := simple.NewDomain(
	facade.GetEventRepository(),
	facade.GetAggregateRepository(),
	simpleDomainOpts...,
)
if err := facade.AddDomain(simpleDomain); err != nil {
	return nil, err
}