Golang port of Greg Young's SimpleCQRS project
TL;DR
I ported Greg Young’s Simple CQRS Example to Go. Check it out here: Golang Version.
Why
Mainly because I wanted to see if I could implement something very similar in Go. In particular, I wanted to see if I could find a way to implement the functionality of the AggregateRoot
abstract class (without using the method overloading that it relies on).
Interesting bits
The C# example uses subclassing and method overloading to make sure the correct logic is called on the aggregate root objects for both commands and events. Unfortunately this is impossible in Go, as this feature is not present in the language. (TBH, I’m not really sure if C# really supports this out of the box in the way this example uses it, as the AsDynamic()
and infrastructure code seems to be using private reflection).
The two areas where this feature is used to great effect are in:
The
ApplyChange(Event, bool)
function within the AggregateRoot// push atomic aggregate changes to local history for further processing (EventStore.SaveEvents) private void ApplyChange(Event @event, bool isNew) { this.AsDynamic().Apply(@event); if(isNew) _changes.Add(@event); }
Which uses the tricky private reflection technique to dispatch events to private handler methods defined on the subclass:
public class InventoryItem : AggregateRoot { private bool _activated; private Guid _id; private void Apply(InventoryItemCreated e) { _id = e.Id; _activated = true; } private void Apply(InventoryItemDeactivated e) { _activated = false; } ...
This is extremely cool, as it allows the subclass to have separated, private methods that handle these events (i.e. they cannot be called by other objects and they have a single clear purpose).
The command and event registration code
var commands = new InventoryCommandHandlers(rep); bus.RegisterHandler<CheckInItemsToInventory>(commands.Handle); bus.RegisterHandler<CreateInventoryItem>(commands.Handle); bus.RegisterHandler<DeactivateInventoryItem>(commands.Handle); bus.RegisterHandler<RemoveItemsFromInventory>(commands.Handle); bus.RegisterHandler<RenameInventoryItem>(commands.Handle); var detail = new InventoryItemDetailView(); bus.RegisterHandler<InventoryItemCreated>(detail.Handle); bus.RegisterHandler<InventoryItemDeactivated>(detail.Handle); bus.RegisterHandler<InventoryItemRenamed>(detail.Handle); bus.RegisterHandler<ItemsCheckedInToInventory>(detail.Handle); bus.RegisterHandler<ItemsRemovedFromInventory>(detail.Handle); var list = new InventoryListView(); bus.RegisterHandler<InventoryItemCreated>(list.Handle); bus.RegisterHandler<InventoryItemRenamed>(list.Handle); bus.RegisterHandler<InventoryItemDeactivated>(list.Handle);
This uses functional overloading to make sure the correct command handler is called in response to commands, and that the correct event processing function is called on the each read model in response to the emitting of new events.
How
My tactic for dealing with these was to :
- Implement a sort of template method pattern in Go.
- Register the command processing functions and event handlers a little more explicitly, and to utilise the Golang reflect package on Command and Event Types.
The code for the template method
I suspected that this was the correct pattern for my problem, and found help on how to achieve it in Go on (of course) StackOverflow.
The ApplyChange(Event, bool)
code looks similar, but utilises the template method InnerApply()
:
// push atomic aggregate changes to local history for further processing (EventStore.SaveEvents)
func (ag *AggRoot) applyChangeInternal(e Event, isNew bool) error {
err := ag.InnerApply(e)
if err != nil {
return err
}
if isNew {
ag.changes = append(ag.changes, e)
}
return nil
}
This InnerApply()
function must be correctly setup in the construction of the struct that is embedded in the AggRoot
struct:
type AggRoot struct {
changes []Event
_version int
id Guid
InnerApply func(e Event) error // Needs public scope
}
type InventoryItem struct {
AggRoot
activated bool
}
// used to create in repository ... many ways to avoid this, eg making private constructor
func NewEmptyInventoryItem() *InventoryItem {
i := &(InventoryItem{
AggRoot: NewEmptyAggRoot(),
})
i.AggRoot.InnerApply = i.handleEvent // IMPORTANT!!! Setup Template Method
return i
}
The private handleEvent()
on the InventoryItem
then uses a type switch to pick out the events it is interested in:
func (ii *InventoryItem) handleEvent(event Event) error {
switch e := event.(type) {
case InventoryItemCreated:
ii.id = e.Id()
ii.activated = true
case InventoryItemDeactivated:
ii.activated = false
}
return nil
}
This is not quite as safe as the C# implementation, as the developer has to make sure they setup the embedded struct and function pointer correctly, but from the outside it keep much the same interface.
The code for the command and event handlers
I chose to separate the registration and storagte of the command handler function and event processing function into two distinct areas. This also allows the code in the “FakeBus” to enforce the “one command handler only” policy a little more clearly:
type CommandHandler func(cmd Command) error
type EventProcessor func(cmd Event) error
type FakeBus struct {
commandHandlers map[reflect.Type]CommandHandler
eventProcessors map[reflect.Type][]EventProcessor
}
func NewFakeBus() FakeBus {
return FakeBus{
commandHandlers: make(map[reflect.Type]CommandHandler),
eventProcessors: make(map[reflect.Type][]EventProcessor),
}
}
func (fb *FakeBus) SetCommandHandler(cmdType reflect.Type, handler CommandHandler) error {
if _, ok := fb.commandHandlers[cmdType]; ok {
return errors.New("command handler already registered")
}
fb.commandHandlers[cmdType] = handler
return nil
}
func (fb *FakeBus) AddEventProcessor(eventType reflect.Type, processor EventProcessor) error {
processors, ok := fb.eventProcessors[eventType]
if !ok {
processors = make([]EventProcessor, 0)
}
for _, p := range processors {
if reflect.DeepEqual(p, processor) {
return errors.New("processor already registered")
}
}
processors = append(processors, processor)
fb.eventProcessors[eventType] = processors
return nil
}
The resulting configuration code is a little more verbose, but ultimately very similar:
bus := s.NewFakeBus(mimicEventualConsistency)
storage := s.NewEventStore(&bus)
rep := s.InventoryItemRepository{storage}
commands := s.NewInventoryCommandHandlers(rep)
bus.SetCommandHandler(reflect.TypeOf(s.CheckInItemsToInventory{}), commands.HandleCheckInItemsToInventory)
bus.SetCommandHandler(reflect.TypeOf(s.CreateInventoryItem{}), commands.HandleCreateInventoryItem)
bus.SetCommandHandler(reflect.TypeOf(s.DeactivateInventoryItem{}), commands.HandleDeactivateInventoryItem)
bus.SetCommandHandler(reflect.TypeOf(s.RemoveItemsFromInventory{}), commands.HandleRemoveItemsFromInventory)
bus.SetCommandHandler(reflect.TypeOf(s.RenameInventoryItem{}), commands.HandleRenameInventoryItem)
bsdb := s.NewBSDB()
detail := s.NewInventoryItemDetailView(&bsdb)
bus.AddEventProcessor(reflect.TypeOf(s.InventoryItemCreated{}), detail.ProcessInventoryItemCreated)
bus.AddEventProcessor(reflect.TypeOf(s.InventoryItemDeactivated{}), detail.ProcessInventoryItemDeactivated)
bus.AddEventProcessor(reflect.TypeOf(s.InventoryItemRenamed{}), detail.ProcessInventoryItemRenamed)
bus.AddEventProcessor(reflect.TypeOf(s.ItemsCheckedInToInventory{}), detail.ProcessItemsCheckedInToInventory)
bus.AddEventProcessor(reflect.TypeOf(s.ItemsRemovedFromInventory{}), detail.ProcessItemsRemovedFromInventory)
list := s.NewInventoryListView(&bsdb)
bus.AddEventProcessor(reflect.TypeOf(s.InventoryItemCreated{}), list.ProcessInventoryItemCreated)
bus.AddEventProcessor(reflect.TypeOf(s.InventoryItemRenamed{}), list.ProcessInventoryItemRenamed)
bus.AddEventProcessor(reflect.TypeOf(s.InventoryItemDeactivated{}), list.ProcessInventoryItemDeactivated)
Conclusion
A Golang port of a pretty cool pattern. I recommend you go checkout the code yourself, there is even a simple way to demonstrate the eventual consistency behaviour mentioned in the README ;-)