7.5 KiB
name, description, user-invocable
| name | description | user-invocable |
|---|---|---|
| ddd | Domain-Driven Design concepts: bounded contexts, aggregates, commands, events, and tactical patterns. Use when analyzing domain models, identifying bounded contexts, or mapping features to DDD patterns. | false |
Domain-Driven Design (DDD)
Strategic and tactical patterns for modeling complex domains.
Strategic DDD: Bounded Contexts
What is a Bounded Context?
A bounded context is a boundary within which a domain model is consistent. Same terms can mean different things in different contexts.
Example: "Order" means different things in different contexts:
- Sales Context: Order = customer purchase with payment and shipping
- Fulfillment Context: Order = pick list for warehouse
- Accounting Context: Order = revenue transaction
Identifying Bounded Contexts
Look for:
- Different language: Same term means different things
- Different models: Same concept has different attributes/behavior
- Different teams: Natural organizational boundaries
- Different lifecycles: Entities created/destroyed at different times
- Different rate of change: Some areas evolve faster than others
From vision/manifesto:
- Identify personas → each persona likely interacts with different contexts
- Identify core domain concepts → group related concepts into contexts
- Identify capabilities → capabilities often align with contexts
From existing code:
- Look for packages/modules that cluster related concepts
- Identify seams where code is loosely coupled
- Look for translation layers between subsystems
- Identify areas where same terms mean different things
Context Boundaries
Good boundaries:
- Clear interfaces between contexts
- Each context owns its data
- Contexts communicate via events or APIs
- Minimal coupling between contexts
Bad boundaries:
- Shared database tables across contexts
- Direct object references across contexts
- Mixed concerns within a context
Common Context Patterns
| Pattern | Description | Example |
|---|---|---|
| Core Domain | Your unique competitive advantage | Custom business logic |
| Supporting Subdomain | Necessary but not differentiating | User management |
| Generic Subdomain | Common problems, use off-the-shelf | Email sending, file storage |
Tactical DDD: Building Blocks
Aggregates
An aggregate is a cluster of entities and value objects treated as a unit for data changes.
Rules:
- One entity is the aggregate root (only entity referenced from outside)
- All changes go through the root
- Enforce business invariants within the aggregate
- Keep aggregates small (2-3 entities max when possible)
Example:
Order (root)
├── OrderLine
├── ShippingAddress
└── Payment
External code only references Order, never OrderLine directly.
Identifying aggregates:
- What entities always change together?
- What invariants must be enforced?
- What is the transactional boundary?
Commands
Commands represent intent to change state. Named with imperative verbs.
Format: [Verb][AggregateRoot] or [AggregateRoot][Verb]
Examples:
PlaceOrderorOrderPlaceCancelSubscriptionorSubscriptionCancelApproveInvoiceorInvoiceApprove
Commands:
- Are handled by the aggregate root
- Either succeed completely or fail
- Can be rejected (return error)
- Represent user intent or system action
Events
Events represent facts that happened in the past. Named in past tense.
Format: [AggregateRoot][PastVerb] or [Something]Happened
Examples:
OrderPlacedSubscriptionCancelledInvoiceApprovedPaymentFailed
Events:
- Are immutable (already happened)
- Can be published to other contexts
- Enable eventual consistency
- Create audit trail
Value Objects
Value Objects are immutable objects defined by their attributes, not identity.
Examples:
Money(amount + currency)EmailAddressDateRangeAddress
Characteristics:
- No identity (two with same values are equal)
- Immutable (cannot change, create new instance)
- Can contain validation logic
- Can contain behavior
When to use:
- Concept has no lifecycle (no create/update/delete)
- Equality is based on attributes, not identity
- Can be shared/reused
Entities
Entities have identity that persists over time, even if attributes change.
Examples:
User(ID remains same even if name/email changes)Order(ID remains same through lifecycle)Product(ID remains same even if price changes)
Characteristics:
- Has unique identifier
- Can change over time
- Identity matters more than attributes
Mapping Features to DDD Patterns
Process
For each feature from vision:
-
Identify the bounded context: Which context does this belong to?
-
Identify the aggregate(s): What entities/value objects are involved?
-
Identify commands: What actions can users/systems take?
-
Identify events: What facts should be recorded when commands succeed?
-
Identify value objects: What concepts are attribute-defined, not identity-defined?
Example: "User can place an order"
Bounded Context: Sales
Aggregate: Order (root)
OrderLine(entity)ShippingAddress(value object)Money(value object)
Commands:
PlaceOrderAddOrderLineRemoveOrderLineUpdateShippingAddress
Events:
OrderPlacedOrderLineAddedOrderLineRemovedShippingAddressUpdated
Value Objects:
Money(amount, currency)Address(street, city, zip, country)Quantity
Refactoring to DDD
When existing code doesn't follow DDD patterns:
Identify Misalignments
Anemic domain model:
- Entities with only getters/setters
- Business logic in services, not entities
- Fix: Move behavior into aggregates
God objects:
- One entity doing too much
- Fix: Split into multiple aggregates or value objects
Context leakage:
- Same model shared across contexts
- Fix: Create context-specific models with translation layers
Missing boundaries:
- Everything in one module/package
- Fix: Identify bounded contexts, separate into modules
Refactoring Strategies
Extract bounded context:
As a developer, I want to extract [Context] into a separate module,
so that it has clear boundaries and can evolve independently
Extract aggregate:
As a developer, I want to extract [Aggregate] from [GodObject],
so that it enforces its own invariants
Introduce value object:
As a developer, I want to replace [primitive] with [ValueObject],
so that validation is centralized and the domain model is clearer
Introduce event:
As a developer, I want to publish [Event] when [Command] succeeds,
so that other contexts can react to state changes
Anti-Patterns
Avoid:
- Aggregates spanning multiple bounded contexts
- Shared mutable state across contexts
- Direct database access across contexts
- Aggregates with dozens of entities (too large)
- Value objects with identity
- Commands without clear aggregate ownership
- Events that imply future actions (use commands)
Tips
- Start with strategic DDD (bounded contexts) before tactical patterns
- Bounded contexts align with team/organizational boundaries
- Keep aggregates small (single entity when possible)
- Use events for cross-context communication
- Value objects make impossible states impossible
- Refactor incrementally - don't rewrite everything at once