---
name: domain-driven-design
description: Apply Domain-Driven Design principles for modeling complex business domains
version: 1.0.0
author: wondelai (adapted)
platforms: [claude-code, cursor]
license: MIT
source: https://github.com/wondelai/skills
based_on: "Domain-Driven Design by Eric Evans"
---

# Domain-Driven Design (DDD)

Apply DDD principles to model complex business domains effectively.

## Scoring System
Rate the design 0-10 on DDD alignment.
Show: current score → issues → improved model → explanation.

## Strategic Design

### Bounded Context
A Bounded Context is a boundary within which a particular domain model applies. Names mean different things in different contexts.

```
Example:
  "Customer" in Sales context = contact with purchasing history
  "Customer" in Support context = ticket submitter with SLA level
  → These are different models, different tables, different services
```

Identify contexts by asking:
- Which teams work on which parts?
- Where do the same words mean different things?
- Where does consistency matter vs where is eventual consistency fine?

### Context Map Patterns
- **Shared Kernel** — two contexts share a subset of the domain model
- **Customer/Supplier** — upstream context defines API, downstream adapts
- **Anti-Corruption Layer** — translation layer to protect your model from a legacy system
- **Published Language** — shared canonical data format between contexts

## Tactical Design

### Aggregates
The root entity that owns consistency:
```python
class Order:  # Aggregate Root
    def __init__(self, order_id: OrderId, customer_id: CustomerId):
        self.id = order_id
        self.customer_id = customer_id
        self._items: list[OrderItem] = []  # child entities
        self._events: list[DomainEvent] = []

    def add_item(self, product: Product, quantity: int) -> None:
        # Business rule enforced here, not in service
        if quantity <= 0:
            raise ValueError("Quantity must be positive")
        self._items.append(OrderItem(product.id, quantity, product.price))
```

Rules:
- Only reference other aggregates by ID, not by object reference
- Transactions should not cross aggregate boundaries
- Keep aggregates small

### Value Objects
Immutable objects defined by their attributes:
```python
@dataclass(frozen=True)
class Money:
    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)
```

### Domain Events
Something that happened in the domain:
```python
@dataclass
class OrderPlaced:
    order_id: str
    customer_id: str
    total_amount: Money
    occurred_at: datetime
```

### Repository Pattern
Abstract persistence from domain logic:
```python
class IOrderRepository(Protocol):
    def get(self, order_id: OrderId) -> Order: ...
    def save(self, order: Order) -> None: ...
    def find_by_customer(self, customer_id: CustomerId) -> list[Order]: ...
```

## Common Anti-Patterns
- **Anemic Domain Model** — entities with only getters/setters, all logic in services
- **Fat Service Layer** — services that contain all business rules
- **God Object** — one entity that knows everything
- **Primitive Obsession** — using `str` for email, `int` for money, etc.