Part 2 · 2 chapters · ~12 min
DDD Tactical Patterns
Entities and value objects, aggregates as consistency boundaries, repositories per aggregate, domain events, domain services, bounded contexts and ubiquitous language, context maps and anti-corruption layers, and where DDD is overkill.
4
Entities, value objects, aggregates
code
// value object: immutable, compared by value, validates itself
export class Money {
private constructor(readonly kobo: bigint, readonly currency: 'NGN' | 'USD') {}
static of(kobo: bigint, c: 'NGN' | 'USD') { if (kobo < 0n) throw new Error('negative'); return new Money(kobo, c); }
plus(o: Money) { this.same(o); return new Money(this.kobo + o.kobo, this.currency); }
private same(o: Money) { if (o.currency !== this.currency) throw new Error('currency mismatch'); }
}
// aggregate root method: rules live here, not in controllers
hold(amount: Money, ref: string): void {
if (this.available().lt(amount)) throw new InsufficientFunds(this.id, amount);
this.holds.push(new Hold(ref, amount)); this.events.push({ type: 'FundsHeld', accountId: this.id, ref });
}AN AGGREGATE AND ITS BOUNDARY
one consistency boundary, changed through its root
swipe the figure sideways, or tap expand for full screen
1/4
the root
An aggregate is a cluster of objects changed together and kept consistent in one transaction. Outside code talks only to the root, which enforces the invariants.
one root guards the rulesinvariant: available ≥ 0
5
Bounded contexts and translation
The word "account" means a ledger account to finance, a login to identity, and a customer to support. A bounded context is the boundary inside which one meaning holds. Between contexts, translate explicitly: an anti-corruption layer maps a partner's or legacy system's model into yours so their naming and quirks do not leak in (a bank rail's status codes become your PayoutState).
| context relationship | meaning |
|---|---|
| customer / supplier | downstream needs are negotiated into the upstream roadmap |
| conformist | downstream adopts upstream's model as is (common with big vendors) |
| anti-corruption layer | downstream translates to protect its model |
| published language | a shared, versioned schema (events, ISO 20022 messages) |
Overkill for CRUD and reporting; worth it in the core domain where rules are subtle and costly to get wrong.