Skip to content

Aggregates

Aggregates make transactional boundaries explicit. An aggregate is a cluster of domain objects treated as a single unit with regard to change: a transaction within an aggregate must be atomic, and a transaction never spans two of them.

The objects in an aggregate map onto the end user’s mental model — the things they believe they are acting on — which is what keeps the boundary defensible when the model is discussed with the business rather than only with developers. The constraint the aggregate adds is that those actions must also be transactional, following the ACID properties.

That constraint is what makes aggregates useful for finding the seams along which a system can be broken into parts. Anywhere a transaction is not required, a boundary is permitted. Aggregates can be identified with ordinary analysis techniques such as use cases, and a service API can then be read as a set of operations, each one a command to an aggregate.

An aggregate enforces its own consistency using invariants. An invariant is a rule that must hold whatever changes are made — an order total that must equal the sum of its lines, a balance that must not go negative. The invariants decide the boundary: whatever must be checked together in one transaction belongs in one aggregate, and whatever does not belongs outside it.

Keeping aggregates small is therefore the goal, not an optimisation. A large aggregate drags unrelated data into the same transaction, and eventually into the same service.