Domain-Driven Design Tactical Patterns
Model complex business domains with entities, value objects, and bounded contexts.
Overview
- Modeling complex business logic
- Separating domain from infrastructure
- Establishing clear boundaries between subdomains
- Building rich domain models with behavior
- Implementing ubiquitous language in code
Building Blocks Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DDD Building Blocks โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ENTITIES VALUE OBJECTS AGGREGATES โ
โ Order (has ID) Money (no ID) [Order]โItems โ
โ โ
โ DOMAIN SERVICES REPOSITORIES DOMAIN EVENTS โ
โ PricingService IOrderRepository OrderSubmitted โ
โ โ
โ FACTORIES SPECIFICATIONS MODULES โ
โ OrderFactory OverdueOrderSpec orders/, payments/ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Quick Reference
Entity (Has Identity)
from dataclasses import dataclass, field
from uuid import UUID
from uuid_utils import uuid7
@dataclass
class Order:
"""Entity: Has identity, mutable state, lifecycle."""
id: UUID = field(default_factory=uuid7)
customer_id: UUID = field(default=None)
status: str = "draft"
def __eq__(self, other: object) -> bool:
if not isinstance(other, Order):
return NotImplemented
return self.id == other.id
def __hash__(self) -> int:
return hash(self.id)
Load Read("${CLAUDE_SKILL_DIR}/references/entities-value-objects.md") for complete patterns.
Value Object (Immutable)
from dataclasses import dataclass
from decimal import Decimal
@dataclass(frozen=True)
class Money:
"""Value Object: Defined by attributes, not identity."""
amount: Decimal
currency: str
def __add__(self, other: "Money") -> "Money":
if self.currency != other.currency:
raise ValueError("Cannot add different currencies")
return Money(self.amount + other.amount, self.currency)
Load Read("${CLAUDE_SKILL_DIR}/references/entities-value-objects.md") for Address, DateRange examples.
Key Decisions
| Decision |
Recommendation |
| Entity vs VO |
Has unique ID + lifecycle? Entity. Otherwise VO |
| Entity equality |
By ID, not attributes |
| Value object mutability |
Always immutable (frozen=True) |
| Repository scope |
One per aggregate root |
| Domain events |
Collect in entity, publish after persist |
| Context boundaries |
By business capability, not technical |
Rules Quick Reference
| Rule |
Impact |
What It Covers |
aggregate-boundaries (load ${CLAUDE_SKILL_DIR}/rules/aggregate-boundaries.md) |
HIGH |
Aggregate root design, reference by ID, one-per-transaction |
aggregate-invariants (load ${CLAUDE_SKILL_DIR}/rules/aggregate-invariants.md) |
HIGH |
Business rule enforcement, specification pattern |
aggregate-sizing (load ${CLAUDE_SKILL_DIR}/rules/aggregate-sizing.md) |
HIGH |
Right-sizing, when to split, eventual consistency |
When NOT to Use
Under 5 entities? Skip DDD entirely. The ceremony costs more than the benefit.
| Pattern |
Interview |
Hackathon |
MVP |
Growth |
Enterprise |
Simpler Alternative |
| Aggregates |
OVERKILL |
OVERKILL |
OVERKILL |
SELECTIVE |
APPROPRIATE |
Plain dataclasses with validation |
| Bounded contexts |
OVERKILL |
OVERKILL |
OVERKILL |
BORDERLINE |
APPROPRIATE |
Python packages with clear imports |
| CQRS |
OVERKILL |
OVERKILL |
OVERKILL |
OVERKILL |
WHEN JUSTIFIED |
Single model for read/write |
| Value objects |
OVERKILL |
OVERKILL |
BORDERLINE |
APPROPRIATE |
REQUIRED |
Typed fields on the entity |
| Domain events |
OVERKILL |
OVERKILL |
OVERKILL |
SELECTIVE |
APPROPRIATE |
Direct method calls between services |
| Repository pattern |
OVERKILL |
OVERKILL |
BORDERLINE |
APPROPRIATE |
REQUIRED |
Direct ORM queries in service layer |
Rule of thumb: DDD adds ~40% code overhead. Only worth it when domain complexity genuinely demands it (5+ entities with invariants spanning multiple objects). A CRUD app with DDD is a red flag.
Anti-Patterns (FORBIDDEN)
@dataclass
class Order:
id: UUID
items: list
class Order:
def save(self, session: Session):
@dataclass
class Money:
amount: Decimal
async def get(self, id: UUID) -> OrderModel:
Related Skills
aggregate-patterns - Deep dive on aggregate design
ork:distributed-systems - Cross-aggregate coordination
ork:database-patterns - Schema design for DDD
References
Load on demand with Read("${CLAUDE_SKILL_DIR}/references/<file>"):
| File |
Content |
entities-value-objects.md |
Full entity and value object patterns |
repositories.md |
Repository pattern implementation |
domain-events.md |
Event collection and publishing |
bounded-contexts.md |
Context mapping and ACL |
Capability Details
entities
Keywords: entity, identity, lifecycle, mutable, domain object
Solves: Model entities in Python, identity equality, adding behavior
value-objects
Keywords: value object, immutable, frozen, dataclass, structural equality
Solves: Create immutable value objects, when to use VO vs entity
domain-services
Keywords: domain service, business logic, cross-aggregate, stateless
Solves: When to use domain service, logic spanning aggregates
repositories
Keywords: repository, persistence, collection, IRepository, protocol
Solves: Implement repository pattern, abstract DB access, ORM mapping
bounded-contexts
Keywords: bounded context, context map, ACL, subdomain, ubiquitous language
Solves: Define bounded contexts, integrate with ACL, context relationships