mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 13:59:36 +01:00
docs: and skills
This commit is contained in:
parent
e4f0665086
commit
ccd4ee534a
25 changed files with 10412 additions and 0 deletions
348
bin/.claude/skills/ddd-model/workflow.md
Normal file
348
bin/.claude/skills/ddd-model/workflow.md
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue