Pros and Cons: Modifying Aggregates Solely Via Events

If you're doing DDD with Event Sourcing you will have two circumstances in which you wish to modify aggregate instances:

  • In response to requests (commands) sent to the system which may or may not be accepted.
  • Rehydration of the aggregate instance from historic events. Depending on implementation this may be required for any use of the instance, on first use or in a rebuild scenario (the trigger is not directly relevant for this discussion)

A naive implementation leads to duplicate modification logic on the aggregate, first to alter the aggregate in response to a command and second to do so when rebuilding from events. This kind of duplication is tedious but also very risky. Any errors means that a rehydrated aggregate instance may not be the same as the original, potentially leading to some very subtle and hard to track bugs.

The solution is relatively simple. The command processing will necessarily produce an event if the state of the aggregate is modified. Rather than having the command handling logic both produce an event and perform modifications to the aggregate instead just produce an event. Then feed this event into the same logic that is used to modify the aggregate instance when rehydrating it. This can be done by the same infrastructure that saves the event or by directly invoking it in the code.

The primary advantage of this is that assuming the events are immutable and are not mangled by the infrastructure you can have a high degree of confidence that the aggregate has been rehydrated into the correct state.

The main disadvantage is that people unfamiliar with the system may find it less clear as to how the aggregate instances are being updated. In general though it's not particularly difficult to explain how this works.

The actual implementation of the updating from events is fairly straightforward. My preferred approach is to have a private method for each event related to the aggregate, each taking that event type as their sole parameter. It is fairly straightforward to build some infrastructure code that will scan an aggregate type and generate a dictionary of handler functions that can be used to invoke the appropriate method (using something like .NET's expression trees. It is also useful to write tests that scan the aggregate types and verify that there are handlers for each event type.

Colin Scott

Read more posts by this author.