# 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/ │ └── / │ ├── 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/ │ └── / │ ├── service.go # Application services / Use cases │ └── dto.go # Data transfer objects └── infrastructure/ └── / ├── 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