SuperAPI AI Directory

aggregate-design

Design & creation

View repository

Updated 10-15 22:39

About this skill

Aggregate Design Guide

Principles of Aggregate design in DDD.

What Is an Aggregate

Aggregate = Consistency Boundary

  • An aggregate is a graph of objects, and that graph is treated as a single unit
  • Within the aggregate,true invariants(constraints that must always be satisfied) are maintained
  • The aggregate root (root entity) is the sole entry point for the entire aggregate

Boundary Example: Car Aggregate

┌─────────────────────────────────────┐
│           Car集約                    │
│  ┌──────────────────────────────┐  │
│  │   Car (集約ルート)            │  │
│  │   - carId: CarId             │  │
│  │   - tires: List[Tire]        │  │
│  │   - engine: Engine           │  │
│  └──────────────────────────────┘  │
│         ↓ 所有               ↓ 所有  │
│  ┌──────────┐         ┌─────────┐ │
│  │  Tire    │ × 4     │ Engine  │ │
│  │ (VoかEnt)│         │ (VoかEnt)│ │
│  └──────────┘         └─────────┘ │
└─────────────────────────────────────┘
  • Tire and Engine are within the boundary of the Car aggregate
  • Direct access to Tire or Engine from outside is not allowed
  • They can be operated only through Car

Design by Contract (DbC)

Aggregates arecontractsdesigned based on.

Contract Description Responsibility
Precondition A condition that must be satisfied before a method call Caller
Postcondition A condition that is satisfied after method execution Implementer
Invariant A condition that must always be satisfied Implementer

For detailed language-specific implementation patterns, see references/typescript.md、references/scala.md、references/rust.md、references/python.md .

Basic Principles

1. Immutability Recommended

In modern development, immutability is recommended. Unless there is a particular reason, make it immutable. When updating state, carry over existing values and overwrite only the fields that change. This prevents omissions when adding fields and makes the intent of updates clear.

Language Immutable Update Pattern
TypeScript Props type + ...this.props spread syntax
Scala case class + copy()
Rust struct + ..self struct update syntax
Python dataclass(frozen=True) + replace()

2. Strong Consistency Boundary

An aggregate is a single strong consistency boundary. All state inside the aggregate is placed under the management of that aggregate.

3. Indirect References to Other Aggregates

Do not hold references to other aggregates inside an aggregate. When relating to other aggregates, reference them indirectly by ID.

4. Complete Constructor

Initialize all state in the primary constructor. Even when overloading, always design auxiliary constructors so that they use the primary constructor.

5. Defensive Copy

When holding mutable objects, always return a copy or convert them to immutable objects when returning them externally.

6. Maintaining Invariants

No matter what operation is performed, it must not fall into an invalid state. For immutable aggregates, protect this with the primary constructor.

Structural Principles

7. Access via the Aggregate Root

Direct access to entities or value objects inside an aggregate must always go through the aggregate root.

8. 1 Transaction = 1 Aggregate

Do not update multiple aggregates in a single transaction. Consistency between aggregates is ensured by eventual consistency.

9. Keep Aggregates Small

An overly large aggregate causes concurrency problems (lock contention). Include only the scope that truly requires consistency.

Collaboration Principles

10. Collaboration via Domain Events

When an aggregate changes state, it publishes a domain event, and other aggregates or external systems subscribe to it and react.

11. Optimistic Locking (Only When Required)

Only when detection of concurrent update conflicts is required should a version number be provided. If there is no requirement, it is unnecessary.

12. Ignorance of Persistence

An aggregate focuses on domain logic and is unaware of how it is stored (the responsibility of the repository).


Evans Rules (Eric Evans)

Aggregate rules from the original DDD text.

Evans Rule 1: Responsibility of the Root Entity

Only the aggregate root can be obtained directly from the repository.

  • External objects cannot hold references into the aggregate's internals
  • The aggregate root can pass the identifiers of internal entities, but only for temporary use
  • The aggregate root is responsible for maintaining the invariants of the entire aggregate

Evans Rule 2: Restrictions on Access to Internal Objects

Entities within the boundary have local identifiers. They need only be unique within the aggregate.

  • Cannot be accessed directly from outside
  • The identifiers of internal entities do not need to be globally unique

Vernon's 4 Rules (Vaughn Vernon)

Design rules from 'Implementing Domain-Driven Design'.

Rule 1: True Invariants at the Consistency Boundary

Design aggregates based on true invariants (Guarantee true invariants within the consistency boundary)

良い例:Order集約
- 注文合計 = 各明細の小計の合計(真の不変条件)
- この計算は即座に正しくなければならない

悪い例:結果整合性で十分な場合
- 在庫数の更新は別の集約で結果整合性

Rule 2: Small Aggregates

Keep aggregates small(Keep aggregates small)

Problems with large aggregates:

  • Increased lock contention
  • Decreased concurrency
  • Increased transaction failure rate
❌ Bad: 大きな集約
Product集約
├── productId
├── name
├── backlogItems: List[BacklogItem]  ← 数百件になる可能性
└── ...

✅ Good: 分割した集約
Product集約              BacklogItem集約
├── productId           ├── backlogItemId
├── name                ├── productId(IDで参照)
└── ...                 └── ...

Rule 3: Reference Other Aggregates by ID

Reference other aggregates by ID only(Reference other aggregates by ID)

The same principle as the aforementioned 'indirect reference to other aggregates'.

Rule 4: Eventual Consistency

Use eventual consistency outside the boundary(Outside the boundary is eventually consistent)

  • Consistency between aggregates is ensured by domain events + eventual consistency
  • Do not update multiple aggregates in a single transaction

Review Checklist

Used when reviewing existing aggregates.

Design by Contract

  • Are preconditions clearly defined (require)
  • Are postconditions satisfied (ensuring)
  • Are invariants maintained in the constructor and after each operation

Basic Principles

  • Is the aggregate immutable, or is there a clear reason why it cannot be made immutable?
  • Does it avoid holding direct references to other aggregates (does it hold only IDs)?
  • Does it provide a complete constructor?
  • Do the auxiliary constructors use the primary constructor?
  • When returning mutable objects, does it make defensive copies?
  • Are the invariants validated in the constructor?

Evans Rules

  • Is only the aggregate root retrieved directly from the repository?
  • Are there no direct references to the inside of the aggregate from the outside?
  • Are the identifiers of internal entities limited to temporary use?

Vernon's Rules

  • Are only true invariants within the aggregate boundary (is there no excessive inclusion)?
  • Is the aggregate sufficiently small (does it not contain collections of hundreds of items)?
  • Are other aggregates referenced only by ID?
  • Is consistency between aggregates ensured by eventual consistency?

Structural Principles

  • Is external access only via the aggregate root?
  • Are there no holes through which internal entities can be manipulated directly?
  • Is the size of the aggregate appropriate (is it not too large)?

Collaboration Principles

  • Does it publish domain events when state changes?
  • If there is a requirement for optimistic locking, is a version number implemented?
  • Even though there is no requirement for optimistic locking, does it not have an unnecessary version number?
  • Is persistence logic not leaking into the aggregate?

Related Skills (Recommended to Read Together)

When using this skill, also refer to the following skills:

  • domain-building-blocks: Design of the elements that compose an aggregate (value objects, entities, domain services)
  • aggregate-transaction-boundary: Relationship between aggregates and transaction boundaries (1 transaction = 1 aggregate rule)
  • cross-aggregate-constraints: Constraint checking between aggregates and design of eventual consistency
  • repository-placement: Where to place the repository interface for an aggregate