--- name: ddd description: > 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. user-invocable: 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: 1. **Different language**: Same term means different things 2. **Different models**: Same concept has different attributes/behavior 3. **Different teams**: Natural organizational boundaries 4. **Different lifecycles**: Entities created/destroyed at different times 5. **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:** - `PlaceOrder` or `OrderPlace` - `CancelSubscription` or `SubscriptionCancel` - `ApproveInvoice` or `InvoiceApprove` **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:** - `OrderPlaced` - `SubscriptionCancelled` - `InvoiceApproved` - `PaymentFailed` **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) - `EmailAddress` - `DateRange` - `Address` **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: 1. **Identify the bounded context**: Which context does this belong to? 2. **Identify the aggregate(s)**: What entities/value objects are involved? 3. **Identify commands**: What actions can users/systems take? 4. **Identify events**: What facts should be recorded when commands succeed? 5. **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:** - `PlaceOrder` - `AddOrderLine` - `RemoveOrderLine` - `UpdateShippingAddress` **Events:** - `OrderPlaced` - `OrderLineAdded` - `OrderLineRemoved` - `ShippingAddressUpdated` **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:** ```markdown As a developer, I want to extract [Context] into a separate module, so that it has clear boundaries and can evolve independently ``` **Extract aggregate:** ```markdown As a developer, I want to extract [Aggregate] from [GodObject], so that it enforces its own invariants ``` **Introduce value object:** ```markdown As a developer, I want to replace [primitive] with [ValueObject], so that validation is centralized and the domain model is clearer ``` **Introduce event:** ```markdown 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