# Aggregate

**In short:** An aggregate is a group of related domain objects treated as one unit. One object, the root, is the only entry point from outside, and it keeps the whole group consistent.

## Explanation

An aggregate is a cluster of domain objects that the system treats as a single unit. (See Domain-Driven Design and Entity and Value Object for these terms.)

**The root.** One object in the cluster acts as the aggregate root. Code outside the aggregate may hold references only to this root. It may not point at any other object inside.

**Why the root matters.** The root is the single entry point for updates. It offers methods that change the aggregate. Because every change passes through it, the root can check the rules that must hold and keep the whole aggregate consistent. If code changed a child object directly, it would bypass those checks.

**Finding the boundary.** Think about transactions. A transaction is a set of changes that must succeed or fail together. Objects that must stay consistent together in one transaction form an aggregate. You usually define an aggregate from the transactions you need.

**Links between aggregates.** Avoid direct navigation from one aggregate to another. Store only the identity of the other aggregate, such as a foreign key (a stored ID that points to another record).

## Example

An online shop has orders. Each order contains order items. Placing an order and changing its items must succeed together, so the order and its items form one aggregate. The order is the root.

Other code asks the order to add or remove an item. It never edits an item directly. The order checks its rules first. A shipment record elsewhere stores only the order's ID, not a link to the order object.

## Trade-offs

Routing every change through the root gives one guarded place for rules. The cost is that callers cannot edit inner objects directly. Referencing other aggregates only by identity keeps boundaries clear. The cost is that you cannot navigate straight from one aggregate to another.

An order aggregate: outside code reaches only the root.

```plantuml
@startuml
class Client
package "Order aggregate" {
  class Order <<root>>
  class OrderItem
}
Client --> Order
Order *-- "many" OrderItem
@enduml
```

## Checks

- Each aggregate names one root object.
- References from outside an aggregate point only to its root.
- Changes to an aggregate go through methods on the root.
- Aggregate boundaries follow the transactions that must be consistent together.
- Other aggregates are referenced by identity (foreign key), not by direct navigation.

## Related

- [Domain-Driven Design](https://arc.fuet.ch/concepts/domain-driven-design/domain-driven-design.md)
- [DDD Building Blocks (Tactical Patterns)](https://arc.fuet.ch/concepts/domain-driven-design/ddd-modeling-elements.md)
- [Entity](https://arc.fuet.ch/concepts/domain-driven-design/entity.md)
- [Value Object](https://arc.fuet.ch/concepts/domain-driven-design/value-object.md)
- [Entity and Value Object](https://arc.fuet.ch/concepts/domain-driven-design/entity-vs-value-object.md)
- [Domain Model](https://arc.fuet.ch/concepts/domain-driven-design/domain-model.md)
- [Bounded Context](https://arc.fuet.ch/concepts/domain-driven-design/bounded-context.md)
- [Cargo Shipping Domain Model](https://arc.fuet.ch/concepts/domain-driven-design/cargo-shipping-domain-model-example.md)

## Sources

- [D D D_ Aggregate](https://martinfowler.com/bliki/DDD_Aggregate.html) — martinfowler.com, accessed 2026-11-09
- [Designing a microservice domain model - .NET | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/microservice-domain-model) — learn.microsoft.com, accessed 2026-11-09
- [Domain-driven design - Wikipedia](https://en.wikipedia.org/wiki/Domain-driven_design) — en.wikipedia.org, accessed 2026-11-09
