DDD Code Review
Overview
Validates code against Domain-Driven Design principles. Use alongside ddd-workflow:code-review-oop for complete review:
- •code-review-oop: SRP, composition, design patterns, dependency injection
- •code-review-ddd: Domain modeling, bounded contexts, tactical patterns
Core principle: Domain layer expresses business concepts in pure Python. Infrastructure concerns stay out. Conditionals in entity methods are a smell — push to composed objects.
Quick DDD Compliance Check
Layered Architecture
- • Domain layer has zero external imports (no SQLAlchemy, requests, etc.)
- • Application layer orchestrates; no business logic
- • Infrastructure implements domain interfaces
- • Dependencies point inward: infrastructure → application → domain
Value Objects
- • Immutable (
@dataclass(frozen=True)) - • Equality by value, not identity
- • Validation in
__post_init__ - • No side effects
Entities
- • Identity-based equality
- • Factory methods for creation (
Entity.create(...)) - • State transitions via State pattern (no conditionals)
- • Emits domain events for significant changes
Aggregates
- • Single entry point (aggregate root)
- • Protects invariants
- • Transactional consistency boundary
- • External references by ID only
Domain Events
- • Past tense naming (
OrderPlaced, notPlaceOrder) - • Immutable (frozen dataclass)
- • Contains all data handlers need
- • Emitted by entities, not services
Repositories
- • Interface defined in domain layer
- • Works with domain models only (not ORM)
- • Implementation in infrastructure layer
- • Specific query methods (not generic
search())
Bounded Contexts
- • Clear boundaries between contexts
- • Shared kernel for cross-context models
- • Anti-corruption layer for external systems
Ubiquitous Language
- • Code uses domain terminology
- • No technical jargon in domain layer
- • Names match domain expert vocabulary
Red Flags
| Red Flag | Problem | Fix |
|---|---|---|
from sqlalchemy in domain | Infrastructure leak | Protocol in domain, impl in infrastructure |
| Mutable value object | Identity confusion | frozen=True dataclass |
if in entity method | Conditional smell | State pattern, composed objects |
| Service modifies entity | Anemic model | Entity coordinates via composition |
repository.search(**kwargs) | Generic query | Specific query methods |
| Aggregate exposes mutable list | Broken invariants | Return immutable views |
| Event created in service | Split behavior | Entity emits events |
| Import from other context | Tight coupling | ACL or shared kernel |
process, handle, do names | Unclear meaning | Ubiquitous language |
Quick Self-Review Questions
- •Dependency Direction: Does domain import from infrastructure? → Move interface to domain
- •Value Object: Same values = equal? → Make immutable, value-based equality
- •Entity Behavior: Conditionals in entity? → Push to State pattern
- •Aggregate Invariant: Can external code break it? → Encapsulate
- •Domain Event: Past tense with all needed data? → Rename, include data
- •Repository: Exposes ORM or generic queries? → Domain models, specific methods
- •Bounded Context: Direct imports from other context? → Add ACL
- •Language: Would domain expert understand? → Use ubiquitous language
Integration with code-review-oop
| Concern | Skill |
|---|---|
| Single Responsibility | code-review-oop |
| Dependency Injection | code-review-oop |
| State/Strategy Patterns | code-review-oop |
| Eliminating Conditionals | code-review-oop |
| Domain Layer Purity | code-review-ddd |
| Value Objects & Entities | code-review-ddd |
| Aggregates & Events | code-review-ddd |
| Bounded Contexts | code-review-ddd |
References (load on demand)
- •Detailed violation/fix examples:
references/violations-and-fixes.md - •REST API architecture guide:
references/rest-api-guide.md
When to Skip
- •CRUD-only code with no business logic
- •Scripts or utilities
- •Prototypes not going to production
- •Pure infrastructure code
For domain model code: Use this checklist.