Aggregate Root – How to Build One for CQRS and Event Sourcing

3  comments

An aggregate root is at the heart of your domain. I am going to dissect a simple implementation of an Aggregate Root and reveal how it works in a CQRS and Event Sourced system. Before we dive in, we need to fly through some terms often used in DDD.

(I’ve put together a very simple example of how an aggregate root works. I’ve included tests to make it even clearer: Grab your copy here:)

Download the sample Aggregate Root code here

Invariant: An invariant describes something that must be true within your design, at all times. The only exception is during a transition to a new state. For example, an employee cannot take more annual leave than they have. This invariance could involve an employee object and a list of holiday objects. The sum of the days in the holiday objects must not exceed the total days holiday allocated. Invariants help us to discover our Bounded Context.

UPDATE: (For those of you reading this from Udi Dahan’s blog, he is right, this is a superficial invariant. It is clearly a simplified example, and in the real world would have many ‘special’ cases. The issue isn’t whether this is a good example of an invariant – it isn’t, but that invariants are helpful in the design of the model.)

Bounded Context: A bounded context groups together a model that may have 1 or many objects. These objects will have invariants within and between them. In the holiday example, the bounded context could include a representation of an employee and their leave record. The job of enforcing the invariants within a bounded context is that of the Aggregate Root which is also an Entity.

Entity: An entity is an object that differs by ID. Take for example, a telephone company. Within that context, a ‘line’ identified by a telephone number would be an entity. The context is important. Within a CRM application, the equivalent of a ‘line’ would not be an entity. The ‘line’ may be a ‘Value Object‘ attached to a customer who is in turn an entity.

If you are new to CQRS you may want to see where an Aggregate Root fits into a typcial CQRS architecture. You can do that here.

How An Aggregate Root Works

The job of an Aggregate Root is to control and encapsulate access to it’s members in such a way as to protect it’s invariants. I took the source code for this example from Greg Young’s “Simplest Possible Thing” on his m-r GitHub project. It is well written and is easy to follow:

public abstract class AggregateRoot
{
  private readonly List _changes = new List();
  public abstract Guid Id { get; }
  public int Version { get; internal set; }

  public IEnumerable GetUncommittedChanges()
  {
  }

  public void MarkChangesAsCommitted()
  {
  }

  public void LoadsFromHistory(IEnumerable history)
  {
  }
}

The first thing to note is that is has an Id. An Aggregate Root is an Entity and will therefore have an Id. Aggregates should have little or no dependencies on outside services.

Imagine how much simpler a class is to design and reason about if it is purely doing it’s thing (i.e. Single Responsibility).

No extra clutter of persistence or similar distractions. At some point however, the outside system will need to know the aggregates state. This is where the GetUncommittedChanges comes into use. Once an aggregate has completed a command  (more on this further down) it will store up the changes in the _changes list. These changes are in the form of an Event or description of what has just happened. On completion of the command, the outside system would then request any uncommitted changes. That list would then saved. On success the MarkChangesAsCommitted method used and the state transition is complete.

private readonly List<Event> _changes = new List<Event>();

public IEnumerable<Event> GetUncommittedChanges()
{
return _changes;
}

public void MarkChangesAsCommitted()
{
_changes.Clear();
}

Processing Commands and Generating Events

This is a two phase process. On receipt of a command, the first task of the aggregate is to ensure it can run the command. In our holiday example above, we would do two things to ensure the invariant remained true. First we would sum the total days holiday the employee has already taken. We would then add the days requested in the command to the total. The final step would check the total did not exceed the maximum allocated days. In other words, before it can generate a state change event, it must ensure that all invariant would still hold true.

public void BookHoliday(BookHoliday command)
{
// Ensure the invariants would not be broken by the command
var totalDaysRequested = (command.EndDate - command.StartDate).TotalDays;
var combinedTotal = totalDaysRequested + _totalDaysTaken;

if (combinedTotal > totalAllocated) throw new NotEnoughHolidaysRemainingException();

// 2nd Phase - Create and commit the event
ApplyChange(new HolidayBooked(command.AggregateId, command.Version, command.UserId, command.StartDate, command.EndDate));
}

The First Phase is not for User Validation

It is tempting to assume that this is the place for user input validation. Tasks like required fields, data ranges and string lengths are not domain logic. User input validation should happen before the system generates a command. You can learn more about how to do validation in a CQRS system here.

Once you have determined the command can be run, you generate an event message. This will containing the data that describes the change. In the holiday request example, we might create a HolidayRequested event. At a minimum, it would contain the aggregate id, the version it was applied at, and then context specific data. In this case the dates from and to (assuming you can only take whole days). At this point, the state of the aggregate has not changed.

The Second Phase – Applying the Change

Now that we have an event, the aggregate can apply the state transition to it’s self. To do this it would call the ApplyChange method (these two methods are defined within the aggregate root).

protected void ApplyChange(Event @event)
{
ApplyChange(@event, true);
}
private void ApplyChange(Event @event, bool isNew)
{
this.AsDynamic().Apply(@event);
if (isNew) _changes.Add(@event);
}

You will notice that it passes the ApplyChange through to a private method. This method has a parameter indicating that it is a new event. This indicator allows us to differentiate between historical events and new events. We capture new events in the _changes list in order for an outside service to grab them and save to disk. The apply method then performs a clever little trick of converting it’s self into a dynamic type. This allows us to  call the private method to finally apply the change (this method is defined within the concrete aggregate).

private void Apply(HolidayBooked e)
{
// State transition happens here
_totalDaysTaken += (e.EndDate - e.StartDate).TotalDays;
}

Loading From History

The final method to cover is LoadFromHistory. Before any processing of commands, the aggregate must be brought up to date. The external service retrieves the list of previous events. Given the aggregate is an entity it is a trivial ‘get by id’ or ‘SELECT * FROM X WHERE ID = …’ database query. The outside service would then pass in the events to the LoadFromHistory method which in turn would apply each event to it’s self. Key here is that these are not new events and should not get added to the _changes list.This is only possible because all commands are pre-checked. On success the aggregate generates the events. You can therefore be confident that the events, when played back in order, will return the aggregate to its current state.

public void LoadsFromHistory(IEnumerable<Event> history)
{
foreach (var e in history) ApplyChange(e, false);
}

Wrapping Up

While small and relatively simple, this abstract class is at the heart of CQRS and event sourcing. Providing a clean mechanism for protecting invariants and encapsulating functionality. The lack of dependencies and the mechanism of function make aggregates easy to unit test. Looking now at the example code, I am surprised how little of it I needed change to serve the production applications I build today. This is just one way to build an Aggregate Root. If you are looking for a non-CQRS apporach to building an Aggregate Root I would recommend an article by Udi Dahan called “Don’t Create Aggregate Roots”(http://www.udidahan.com/2009/06/29/dont-create-aggregate-roots/).


Tags

aggregate-root, cqrs, es, event


You may also like

CQRS + Event Sourcing – Step by Step

CQRS + Event Sourcing – Step by Step

Are You Making These 10 DDD Mistakes?

Are You Making These 10 DDD Mistakes?
    • Thanks for pointing this out. I have updated the post with a note to that effect. The issue isn’t that the the employee holiday entitlement is a good example of an invariant, but that invariants are a helpful aid to finding the boundaries of the bounded context.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >