DDD & co., part 4: Aggregates

tl;dr: An aggregate is responsible for consistency in an area of the application, which is why aggregates are also referred to as transactional consistency boundaries. Small aggregates are preferable for good scalability.

Whoever deals with DDD will sooner or later encounter the concept of aggregates. Aggregates are the basis for transforming commands into domain events and therefore play a key role. At the same time, however, they are often the most difficult to understand concept of DDD.

In fact, aggregates can be explained very simply and graphically. If a user sends commands to the system, domain events are created. This transformation is carried out according to certain rules that are intended to ensure consistency within the system.

It is important to clarify which changes are subject to which rules. While some changes are independent of each other and may therefore take place at the same time, others must be carried out one after the other in a controlled manner, as they could influence each other. One of the most important questions in DDD is therefore which changes need to be made in a common transaction and which changes can be carried out separately.

Small transactions promote scalability

The smaller a transaction is, the better the system scales. This is because simultaneous changes do not get in the way of each other. Smaller transactions also mean less impact of changes in the application. You have to watch out not to make a transaction too small, as otherwise the consistency suffers - but not too big as well, as otherwise the scalability suffers.

An aggregate acts as such a transaction boundary and is therefore also called a transactional consistency boundary. Aggregates are often implemented as objects, but this is not mandatory. They could just as well be implemented purely as functions.

Based on the example of the to-do list, the question arises as to whether the individual tasks are independent aggregates or whether the entire list represents a single aggregate. There are arguments for and against their use for both variants:

  • If you define the list as a single aggregate, this has the advantage that you can guarantee consistency within the list. This ensures, for example, that the list never contains more than five not yet ticked off to-dos at any point in time.
  • At the same time, however, this also means that commands such as note, tick off and resume cannot be executed at the same time, since they affect the same transactional consistency limit. This reduces performance when many users want to access the list at the same time.
  • If this scalability is important, this rather suggests that each individual to-do should be an independent aggregate. The commands note, tick off and resume then work independently of each other for different to-dos and can therefore be executed simultaneously.
  • This in turn means that it will be more difficult to guarantee the consistency of the list.

Aggregates in TodoMVC

Since there are no rules that affect the consistency of the list in the TodoMVC application, which was selected as an example, there is no reason to implement them as a single large aggregate. In fact, there is actually only one list, which is why it does not even need to be modeled.

This is also reflected in the previously defined domain events and commands, which all relate exclusively to individual to-dos. It is remarkable in the graphical user interface that it is possible to tick off all to-dos at once. Isn't this a hint that the list should be modeled as a single large aggregate after all?

In fact, the answer to this question depends on how the command is understood. For example, what should happen if some of the to-dos cannot be marked as completed due to a technical error, for example? Should the remaining ones be ticked off anyway, or must they be resumed again?

Responding to partial errors

In the first case, the possibility of tick off all to-dos would be nothing more than a convenience function that saves the user from having to tick off each to-do individually. In the second case, it would be an independent concept of the domain. In this case, however, the current model would lack domain events that describe this case.

The question of how to deal with partial defects is often helpful in deciding how small or large aggregates should be designed. As a rule of thumb, however, it can be assumed that small aggregates are usually advantageous.

Ultimately, the question which of these two approaches is preferable cannot be answered from a technical point of view: Since it is a domain question it must therefore be addressed by a domain expert. In the absence of an authoritative answer for TodoMVC, it is assumed that partial errors are acceptable and therefore an aggregate for the list is fine.

The to-do aggregate

What remains is a small and manageable aggregate that contains the logic for a single to-do. A good name for this aggregate is todo, so that the following structure is created:

  • planning (bounded context)
    • todo (aggregate)
      • notenote
      • editedit
      • tick offticked off
      • resumeresumed
      • discarddiscarded
      • archivearchived

This completes the modeling of TodoMVC. The next question is how to implement this domain model in code using DDD. The first step is to find out how the model can be persisted. A potential approach is the concept of event-sourcing, which will be discussed in the next episode.

Twitter Facebook LinkedIn

Golo Roden

Founder, CTO, and managing partner

Since we want to deliver elegant yet simple solutions of high quality for complex problems, we care about details with love: things are done when they are done, and we give them the time they need to mature.