An aggregate is a cluster of domain objects treated as a single unit for data changes, and the aggregate root is the single entity through which all external access to the aggregate occurs. Outside objects can only reference the root, and all modifications to the aggregate’s internal objects must go through the root, which enforces business invariants and consistency rules.

How It Works

Consider an Order aggregate. The Order (root) contains OrderLineItems. External code cannot directly modify a line item - it must call methods on the Order (addItem, removeItem, changeQuantity). The Order root validates business rules (minimum order amount, maximum quantity, inventory availability) before allowing changes. When saved, the entire aggregate is persisted as a unit within a single transaction.

The key rule: one transaction modifies one aggregate. Cross-aggregate consistency is achieved through eventual consistency (domain events), not through transactions that span multiple aggregates.

Why It Matters

Aggregates define the transactional boundary in your domain model. Getting aggregate boundaries right determines whether your system maintains data consistency, performs well under load, and scales horizontally.

Too large - an aggregate that encompasses too many entities creates contention (many operations lock the same aggregate) and performance problems (loading and saving a large aggregate for small changes).

Too small - aggregates that are too small cannot enforce business invariants that span multiple entities, pushing consistency logic into application services where it is harder to manage.

Practical Guidance

Design aggregates around true invariants - business rules that must be enforced atomically. If two entities must always be consistent with each other in the same transaction, they belong in the same aggregate. If eventual consistency is acceptable between them, they belong in separate aggregates.

Keep aggregates small. Reference other aggregates by ID, not by direct object reference. Use domain events to coordinate between aggregates. This design enables horizontal scaling because different aggregates can be stored on different nodes without cross-node transactions.

Sources

  • Evans, E. (2003). Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley. (Original definition of aggregate, aggregate root, and the invariant enforcement principle; Part III, Chapter 6.)
  • Vernon, V. (2013). Implementing Domain-Driven Design. Addison-Wesley. (Practical guidance on aggregate design; rule of one transaction per aggregate; designing small aggregates.)
  • Fowler, M. (2014). Aggregate. martinfowler.com. (Concise reference for aggregate pattern; clarifies the distinction between aggregate roots and internal entities.)