domain-driven-design

DDD tactical patterns for complex business modeling including entities, value objects, aggregates, domain services, repositories, specifications, and bounded…

INSTALLATION
npx skills add https://github.com/yonatangross/orchestkit --skill domain-driven-design
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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  # Identity equality

    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)  # MUST be frozen!

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)

# NEVER have anemic domain models (data-only classes)

@dataclass

class Order:

    id: UUID

    items: list  # WRONG - no behavior!

# NEVER leak infrastructure into domain

class Order:

    def save(self, session: Session):  # WRONG - knows about DB!

# NEVER use mutable value objects

@dataclass  # WRONG - missing frozen=True

class Money:

    amount: Decimal

# NEVER have repositories return ORM models

async def get(self, id: UUID) -> OrderModel:  # WRONG - return domain!

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

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card