Aggregates and Aggregate Roots
An aggregate groups domain objects whose invariants must be maintained together, and its aggregate root controls access to that group. Choose boundaries based on consistency needs rather than database structure.
An aggregate is a cluster of domain objects treated as a unit for enforcing selected business invariants. It has one aggregate root: the entity through which outside code obtains and changes the aggregate. The root may delegate work to internal entities and value objects, but it remains responsible for keeping the aggregate valid.
The boundary is primarily about consistency and ownership, not about mirroring tables or object relationships. Objects inside the boundary can refer to each other directly. From outside, references should normally target the root; in particular, another aggregate should usually retain the root's identity rather than a direct mutable child reference.
When a use case changes an aggregate, it loads and invokes the root, then persists the aggregate as a unit according to the application's transaction model. This commonly means one transaction per aggregate boundary, but the exact database transaction strategy depends on the system. Invariants spanning multiple aggregates are often coordinated through domain services, application workflows, or eventual consistency rather than by enlarging every boundary.
Choose boundaries around rules that must hold immediately together, and keep them as small as practical. A boundary that is too broad creates contention and expensive loading; one that is too narrow can leave important rules unenforced or require coordination across aggregates. Aggregates are a domain-modeling design choice, not a prescribed database schema.
Key points
- An aggregate is a consistency boundary around related domain objects.
- The aggregate root is the sole normal entry point for external access and mutation.
- Choose boundaries around invariants that must hold immediately together.
- Other aggregates usually refer to a root by identity, not to its mutable children.
- Aggregate boundaries guide domain behavior, not database table layout.
Interview questions
What is an aggregate, and what does its root do?
An aggregate is a group of domain objects whose consistency rules are managed as one unit. Its root is the externally accessible entity that guards those rules and mediates updates to internal members. Other aggregates should normally reference it by identity rather than holding mutable references to its children. This keeps invariant enforcement and lifecycle ownership explicit.
How do you decide where one aggregate ends and another begins?
Start with business invariants: determine which facts must be true immediately after a command completes. Put the objects needed to enforce those rules together, and expose mutations through a root that can validate them. Keep the boundary no larger than necessary because loading and concurrent updates become more costly as it grows. Rules that can tolerate delay or coordination can often cross aggregate boundaries.
Does an aggregate have to map to one database transaction or table?
An aggregate provides a domain-level consistency and ownership boundary; it does not dictate a particular relational schema or guarantee that every persistence operation uses one specific transaction mechanism. In many designs, a command loads a root, applies behavior, and commits its changes in a transaction scoped to that aggregate. If an invariant truly requires atomic updates across several roots, reconsider the model or use explicit coordination, while accounting for concurrency and failure behavior. Avoid silently relying on an ORM graph update to define the domain boundary.
How do aggregates relate to repositories?
A repository is typically designed to retrieve and persist aggregate roots, not arbitrary internal children as independent domain objects. The root's methods preserve invariants while application code performs a use case. A repository may hide persistence details, but it should not become a second mutation path around the root. The exact repository interface depends on the application and persistence technology.