Domain-Driven Design (DDD) Cheat Sheet
Covers core DDD building blocks — entities, value objects, aggregates, repositories, domain events — plus bounded contexts and the ubiquitous language.
Entities vs Value Objects
Entities have identity that persists through change; value objects are defined entirely by their attributes.
// Entity: identity matters, attributes can changeclass Order { constructor( readonly id: OrderId, // identity — never changes private items: OrderLine[], private status: OrderStatus, ) {} addLine(line: OrderLine) { this.items.push(line); }}// Value Object: no identity, immutable, equality by valueclass Money { constructor(readonly amount: number, readonly currency: string) {} equals(other: Money): boolean { return this.amount === other.amount && this.currency === other.currency; } add(other: Money): Money { if (other.currency !== this.currency) throw new Error('currency mismatch'); return new Money(this.amount + other.amount, this.currency); }}
Aggregates & Repositories
An aggregate is a consistency boundary with one root entity; repositories load/persist whole aggregates.
// Aggregate root: the only entry point for mutating the aggregateclass ShoppingCart { private constructor( readonly id: CartId, private lines: CartLine[], ) {} static create(id: CartId): ShoppingCart { return new ShoppingCart(id, []); } addItem(productId: ProductId, qty: number): void { if (qty <= 0) throw new Error('quantity must be positive'); // invariant this.lines.push(new CartLine(productId, qty)); }}interface CartRepository { findById(id: CartId): Promise<ShoppingCart | null>; save(cart: ShoppingCart): Promise<void>;}
Domain Events
Model significant business occurrences explicitly, decoupling side effects from the aggregate that raised them.
class OrderPlaced { readonly occurredAt = new Date(); constructor(readonly orderId: OrderId, readonly total: Money) {}}class Order { private events: DomainEvent[] = []; place(): void { this.status = OrderStatus.Placed; this.events.push(new OrderPlaced(this.id, this.total)); } pullEvents(): DomainEvent[] { const events = this.events; this.events = []; return events; }}// Application layer publishes events after the transaction commitsconst events = order.pullEvents();events.forEach(e => eventBus.publish(e));
DDD Glossary
Core vocabulary from Eric Evans' book and the community that followed.
- Ubiquitous Language- shared vocabulary between developers and domain experts, used in code and conversation
- Bounded Context- an explicit boundary within which a model and its language are consistent
- Entity- object defined by continuity of identity, not attributes
- Value Object- object defined by its attributes, immutable, no identity
- Aggregate / Aggregate Root- cluster of objects treated as one consistency/transaction boundary
- Repository- abstraction for loading/persisting whole aggregates, hides storage details
- Domain Event- immutable record of something significant that happened in the domain
- Context Mapping- explicit patterns (shared kernel, anti-corruption layer, etc.) for context relationships
Keep aggregates small — one aggregate should protect exactly one true business invariant; reaching for a giant aggregate 'to be safe' is the most common DDD mistake and it kills both performance and concurrency.