Skip to content
Yves Reynhout edited this page Dec 9, 2013 · 12 revisions

AggregateSource Core

The core library (AggregateSource) contains interfaces so both testing and event storage can communicate with your aggregates. Think of aggregates as lines you've drawn around one or more entities. The line is a boundary and carries a meaning: consistent on the inside, eventually consistent on the outside. There is usually only one entity within the aggregate that calling code communicates directly with. It's called the aggregate root entity a.k.a. the aggregate root. Next to the root entity you may have other entities and value objects within the boundary of an aggregate.

Aggregate Root Entity

The following picture should clarify how an event is born, as a side effect of invoking a method on the aggregate root.

AggregateRoot: the birth of an event

Editable form

For a new entity this flow is a bit more complicated.

NewEntity: the birth of an event

Editable form

A similar flow exists for an existing entity.

ExistingEntity: the birth of an event

Editable form

Interfaces

IAggregateChangeTracker

An interface that provides the contract for getting the tracked changes of the aggregate. Typically, commands will induce one or more events. It's these events - applied on the aggregate root entity - that will end up as a tracked change.

IAggregateInitializer

An interface that provides the contract for performing aggregate initialization. Typically this involves replaying an enumeration of events onto the aggregate root entity which reconstructs the aggregate's state, including the root, any value objects and/or entities, however deep you may choose them to be.

IAggregateRootEntity

An interface that aggregates IAggregateInitializer and IAggregateChangeTracker interfaces. This is the interface the library interacts the most with.

Implementation

Eventhough these interfaces are pretty strict, there are many ways you could implement them. Reimplementing them on each and every aggregate will probably introduce too much boilerplate and distract the reader of the aggregate's code from what the actual domain behavior is. Most people tend to create a base class that contains the code that is common to all eventsourced aggregates. Nonetheless, over the years, a lot of different implementation styles have emerged. This is where the AggregateSource.Content.* nuget packages come in. I invite you to look at them and pick the one that suits you best. Because this is one of the primary points of integration between your code and this library, I urge you to consider the trade-offs of each approach.

AggregateSource.Content.ExplicitRouting

Requires you to register a callback handler for each event that changes the aggregate's state. This can also be seen as an advantage, you only have to register callback handlers for events that actually change the internal state of the aggregate. Those that don't need to change the internal state do not require a callback handler. Another advantage is that you can use an Action<TEvent> instead of a dedicated method as callback handler which slightly reduces the amount of code to read and type.

AggregateSource.Content.ExplicitStateExplicitRouting

The state of your aggregate is stored in an object under your control, yet separate from the aggregate itself. Events are applied to this separate state object, not the class you code the behavior in. The advantage is that it brings clarity, you only see behavior. Requires you to register a callback handler for each event that changes the aggregate's state. This can also be seen as an advantage, you only have to register callback handlers for events that actually change the internal state of the aggregate. Those that don't need to change the internal state do not require a callback handler. Another advantage is that you can use an Action<TEvent> instead of a dedicated method as callback handler which slightly reduces the amount of code to read and type. For convenience an EntityState base class is provided, furthermore reducing the amount of boilerplate.

Summary

Next to an AggregateRootEntity class, each of these packages also contains an Entity implementation that follows the same design, as a convenience. These packages are content-based, meaning they will add code to the project you install them in.

It's important to realize these packages are not the only implementation one could come up with. In the future I hope to add convention based ones, where the state handlers are discovered at runtime.