Aggregate

In domain-driven design (DDD), an aggregate is a group of domain objects that are treated as a single unit. An aggregate defines a consistency boundary, which means that all changes to the objects within the aggregate must be made in a way that maintains the consistency and integrity of the objects.

An aggregate has a root, which is the main object in the aggregate, and one or more child objects. The root is responsible for maintaining the consistency and integrity of the aggregate, and it controls access to the child objects.

For example, in an e-commerce domain, an aggregate might be an order, which consists of an order header object and one or more order line item objects. The order header object would be the root of the aggregate, and it would be responsible for maintaining the consistency of the order, such as ensuring that the order total is correct, and that the order is in a valid state.

An aggregate should have a small and consistent size and should be designed to be as small as possible, this is to ensure that the complexity of the aggregate is manageable, and that the consistency can be easily maintained.

In DDD, aggregates are used to model the consistency boundaries within the domain and to ensure that the state of the domain is always consistent. They are also used to control access to the child objects, and to provide a way to perform operations that involve multiple objects in the aggregate.

When designing an aggregate, it’s important to identify the consistency boundaries, to choose the appropriate root object, and to ensure that the aggregate is small and consistent.

Aggregates versus objects in OOP

In object-oriented programming (OOP), an object is a self-contained unit of behavior and data that represents a single instance of a class. Objects have a state and behavior, and they can interact with other objects by sending and receiving messages.

In domain-driven design (DDD), an aggregate is also a group of objects, but it has a specific role and purpose. An aggregate defines a consistency boundary, which means that all changes to the objects within the aggregate must be made in a way that maintains the consistency and integrity of the objects.

An aggregate has a root, which is the main object in the aggregate and one or more child objects. The root is responsible for maintaining the consistency and integrity of the aggregate, and it controls access to the child objects. An aggregate should have a small and consistent size and should be designed to be as small as possible, this is to ensure that the complexity of the aggregate is manageable, and that the consistency can be easily maintained.

In contrast to OOP, where objects are self-contained units of behavior and data, in DDD, the aggregate is a pattern that is used to create a consistency boundary, and to ensure the integrity of the domain. It helps to manage the complexity of the domain, by breaking it down into smaller, more manageable parts.

In summary, OOP’s object is a self-contained unit of behavior and data, whereas, DDD’s aggregate is a pattern.

State-stored vs Event-sourced aggregates

Aggregates can be modeled in two ways, either by storing their current state as-is or by constructing the state from the past domain events. State-stored aggregates and event-sourced aggregates are both ways to model domain objects in a domain-driven design (DDD) context.

In state-stored aggregates, the current state of the aggregate is stored in memory or in a database, and changes to the state are made by calling methods on the aggregate. This approach is more familiar to developers who are used to traditional object-oriented programming, as it closely mirrors the way objects work in most programming languages.

In contrast, event-sourced aggregates store the full history of all changes to the aggregate as a sequence of events. To change the state of the aggregate, a new event is added to the event stream, rather than directly modifying the state. This approach allows for more flexibility in handling concurrency and recoverability, and it also allows for better auditing and debugging of the system.

Event sourcing is more complex than state stored approach, but arguably provides more benefits in terms of scalability, fault-tolerance, and auditability.

Aggregate vs. aggregate root vs. entity

An aggregate is a cluster of related objects that are considered as one unit with respect to data changes. An aggregate root is the main object within an aggregate that acts as a gatekeeper to ensure the consistency and integrity of the aggregate as a whole. An entity is a unique object within a domain that represents a real-world object and has a unique identifier.

The difference between an aggregate and an aggregate root is that an aggregate is a group of objects that are treated as one unit, while the aggregate root is the main object that acts as the entry point to the aggregate and is responsible for enforcing the aggregate’s invariants. An entity, on the other hand, is a unique object within a domain that represents a real-world object and can be part of an aggregate.

For example, consider a banking application. An aggregate in this domain might be a customer account, which includes related objects such as the customer information, their transactions, and their account balance. The aggregate root in this case would be the customer account object, which is responsible for enforcing the rules around the balance (e.g. it should never be negative). When changes are made to the account, they are made through the aggregate root, which ensures that the rules are upheld.