Add complete DDD capability set for breaking down product vision into implementation issues using Domain-Driven Design principles. Components: - issue-writing skill: Enhanced with user story format and vertical slices - ddd skill: Strategic and tactical DDD patterns (bounded contexts, aggregates, commands, events) - ddd-breakdown skill: User-invocable workflow (/ddd-breakdown) - ddd-analyst agent: Analyzes manifesto/vision/code, generates DDD-structured user stories Workflow: Read manifesto + vision → analyze codebase → identify bounded contexts → map features to DDD patterns → generate user stories → create Gitea issues Co-Authored-By: Claude Code <noreply@anthropic.com>
273 lines
7.5 KiB
Markdown
273 lines
7.5 KiB
Markdown
---
|
|
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
|