1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 13:59:36 +01:00
effigenix/bin/.claude/skills/ddd-model/workflow.md
2026-02-18 23:25:12 +01:00

348 lines
11 KiB
Markdown

# DDD Modeling Workflow
This document provides detailed instructions for each phase of the DDD modeling workflow.
## Phase 1: Domain Discovery
### Step 1.1: Gather Domain Information
Use AskUserQuestion to understand the domain:
```json
{
"question": "What domain or subdomain are you modeling?",
"header": "Domain",
"multiSelect": false,
"options": [
{"label": "New domain", "description": "Starting from scratch with a new business domain"},
{"label": "Existing domain", "description": "Refactoring or extending an existing domain"},
{"label": "Subdomain extraction", "description": "Extracting a bounded context from a monolith"}
]
}
```
### Step 1.2: Classify Subdomain Type
```json
{
"question": "What type of subdomain is this?",
"header": "Type",
"multiSelect": false,
"options": [
{"label": "Core (Recommended)", "description": "Competitive advantage, complex business logic, high DDD investment"},
{"label": "Supporting", "description": "Necessary for Core, moderate complexity, simplified DDD"},
{"label": "Generic", "description": "Commodity functionality, low complexity, CRUD is fine"}
]
}
```
### Step 1.3: Determine DDD Investment Level
Based on subdomain type:
| Subdomain Type | DDD Investment | Patterns to Use |
|----------------|----------------|-----------------|
| Core | Full | Aggregates, Domain Events, Domain Services, CQRS, Event Sourcing (optional) |
| Supporting | Simplified | Aggregates, basic Value Objects, simple Domain Services |
| Generic | Minimal | CRUD, Transaction Script, Active Record |
### Step 1.4: Identify Business Processes
Ask about key business processes:
```json
{
"question": "What are the main business processes in this domain?",
"header": "Processes",
"multiSelect": true,
"options": [
{"label": "Create/Register", "description": "Creating new domain entities"},
{"label": "Update/Modify", "description": "Changing existing entities"},
{"label": "Workflow/State machine", "description": "Multi-step processes with state transitions"},
{"label": "Calculations", "description": "Complex business calculations or rules"}
]
}
```
---
## Phase 2: Bounded Contexts
### Step 2.1: List Domain Concepts
Ask the user to list key concepts:
"List the main concepts/nouns in your domain. For example: Account, Transaction, Customer, Payment, etc."
### Step 2.2: Group Concepts into Bounded Contexts
Look for:
- Concepts that share the same ubiquitous language
- Concepts that change together
- Concepts with the same lifecycle
- Natural boundaries (teams, deployability)
### Step 2.3: Propose BC Boundaries
Present a Context Map diagram:
```
Example Context Map:
┌──────────────────────────────────────────────────────────┐
│ CORE DOMAIN │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Accounts │─────────>│ Transfers │ │
│ │ │ Customer │ │ │
│ │ - Account │ Supplier│ - Transfer │ │
│ │ - Balance │ │ - Payment │ │
│ └─────────────┘ └─────────────┘ │
│ │ │ │
│ │ Conformist │ Partnership │
│ v v │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Fees │ │ Loyalty │ │
│ │ (Supporting)│ │ (Core) │ │
│ └─────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────────┘
```
### Step 2.4: Define Ubiquitous Language
For each BC, create a glossary:
```markdown
## Accounts BC - Ubiquitous Language
| Term | Definition |
|------|------------|
| Account | A financial account owned by a customer |
| Balance | Current amount of money in the account |
| Holder | Person or entity that owns the account |
| Freeze | Temporarily block all operations on account |
```
### Step 2.5: Map Context Relationships
Define relationships between BCs:
- **Partnership**: Teams cooperate, shared evolution
- **Customer-Supplier**: Upstream provides, downstream consumes
- **Conformist**: Downstream adopts upstream model as-is
- **Anti-corruption Layer**: Downstream translates upstream model
- **Open Host Service**: Upstream provides well-defined protocol
- **Published Language**: Shared language (XML schema, Protobuf)
---
## Phase 3: Tactical Modeling
### Step 3.1: Identify Entities
```json
{
"question": "Which concepts have a unique identity that persists over time?",
"header": "Entities",
"multiSelect": true,
"options": [
{"label": "User/Customer", "description": "Has ID, identity matters even if attributes change"},
{"label": "Order/Transaction", "description": "Tracked by ID throughout lifecycle"},
{"label": "Account/Wallet", "description": "Unique identifier, state changes over time"},
{"label": "Other (specify)", "description": "I'll describe other entities"}
]
}
```
### Step 3.2: Identify Value Objects
```json
{
"question": "Which concepts are defined purely by their values (no identity)?",
"header": "Value Objects",
"multiSelect": true,
"options": [
{"label": "Money/Amount", "description": "$100 = $100, no unique identity"},
{"label": "Address/Location", "description": "Same address values = same address"},
{"label": "DateRange/Period", "description": "Defined by start and end dates"},
{"label": "Email/Phone", "description": "Value-based, immutable"}
]
}
```
### Step 3.3: Identify Aggregates
Decision questions:
1. "What entities must always be consistent together?"
2. "What is the smallest unit that must be loaded together?"
3. "What defines a transaction boundary?"
```json
{
"question": "Which entity should be the Aggregate Root (entry point)?",
"header": "Aggregate Root",
"multiSelect": false,
"options": [
{"label": "Account", "description": "Controls Balance, Transactions within it"},
{"label": "Order", "description": "Controls OrderLines, ShippingInfo"},
{"label": "Customer", "description": "Controls Addresses, Preferences"},
{"label": "Other", "description": "Different aggregate root"}
]
}
```
### Step 3.4: Define Aggregate Boundaries
For each aggregate, determine:
- **Root Entity**: The entry point (only public access)
- **Child Entities**: Internal entities (private, accessed via root)
- **Value Objects**: Immutable data within aggregate
- **Invariants**: Rules that must always hold
Example:
```
Account Aggregate
├── Account (Root)
│ ├── AccountID (VO)
│ ├── Balance (VO)
│ ├── Status (VO/Enum)
│ └── Holders[] (Entity)
│ ├── HolderID (VO)
│ └── Role (VO)
└── Invariants:
- Balance >= 0 (for standard accounts)
- At least one holder with OWNER role
- Cannot debit frozen account
```
---
## Phase 4: Invariants
### Step 4.1: Gather Business Rules
```json
{
"question": "What business rules must ALWAYS be true for this aggregate?",
"header": "Rules",
"multiSelect": true,
"options": [
{"label": "Non-negative values", "description": "Balance, quantity, amount >= 0"},
{"label": "Required relationships", "description": "Must have at least one X"},
{"label": "State constraints", "description": "Cannot do Y when in state Z"},
{"label": "Consistency rules", "description": "Sum of parts equals total"}
]
}
```
### Step 4.2: Formalize Invariants
Convert business rules to formal invariants:
```go
// Invariant: Account balance cannot be negative for standard accounts
// Invariant: Account must have at least one holder with OWNER role
// Invariant: Frozen account cannot process debit operations
// Invariant: Transfer amount must be positive
// Invariant: Source and destination accounts must be different
```
### Step 4.3: Map Invariants to Enforcement Points
| Invariant | Enforcement Point |
|-----------|-------------------|
| Balance >= 0 | `Account.Debit()` method |
| At least one owner | `Account` constructor, `RemoveHolder()` |
| No debit when frozen | `Account.Debit()` method |
| Positive amount | `Money` constructor |
---
## Phase 5: Code Generation
### Step 5.1: Create Folder Structure
Use `templates/folder-structure.md` to create:
```
internal/
├── domain/
│ └── <bc>/
│ ├── aggregate.go # Aggregate roots
│ ├── entity.go # Child entities
│ ├── value_objects.go # Value objects
│ ├── repository.go # Repository interfaces
│ ├── errors.go # Domain errors
│ └── events.go # Domain events (optional)
├── application/
│ └── <bc>/
│ ├── service.go # Application services / Use cases
│ └── dto.go # Data transfer objects
└── infrastructure/
└── <bc>/
├── postgres_repository.go
└── memory_repository.go
```
### Step 5.2: Generate Domain Code
For each aggregate, use templates:
1. `templates/aggregate.go.md` - Generate aggregate root
2. `templates/entity.go.md` - Generate child entities
3. `templates/value-object.go.md` - Generate value objects
4. `templates/repository.go.md` - Generate repository interface
### Step 5.3: Apply Uber Go Style Guide
- Use pointer receivers for Aggregates/Entities
- Use value receivers for Value Objects
- Add compile-time interface checks
- Create domain-specific error types
- Use constructor functions with validation
---
## Phase 6: Validation
### Step 6.1: Run DDD Checklist
Read `rules/ddd-rules.md` and check each rule:
```markdown
### Aggregate Validation
- [ ] Aggregate Root is the only public entry point
- [ ] Child entities are not directly accessible
- [ ] All changes go through Aggregate Root methods
- [ ] Invariants checked in constructor
- [ ] Invariants checked in mutation methods
- [ ] No direct references to other Aggregates
- [ ] References to other Aggregates use ID only
```
### Step 6.2: Output Validation Report
Generate a validation report:
```markdown
## DDD Validation Report
### ✅ Passed
- Aggregate boundaries are clear
- Value Objects are immutable
- Repository interfaces in domain layer
### ⚠️ Warnings
- Consider extracting X into separate Value Object
- Y method might be better as Domain Service
### ❌ Issues
- Direct reference to other Aggregate (should use ID)
- Invariant not enforced in Z method
```
### Step 6.3: Suggest Improvements
Based on validation, suggest:
- Missing Value Objects
- Potential Domain Events
- Domain Services that might be needed
- Possible CQRS opportunities