domain-driven-design by yonatangross/orchestkit
npx skills add https://github.com/yonatangross/orchestkit --skill domain-driven-design使用实体、值对象和有界上下文来建模复杂的业务领域。
┌─────────────────────────────────────────────────────────────┐
│ 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/ │
└─────────────────────────────────────────────────────────────┘
from dataclasses import dataclass, field
from uuid import UUID
from uuid_utils import uuid7
@dataclass
class Order:
"""实体:具有标识、可变状态和生命周期。"""
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)
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
加载 Read("${CLAUDE_SKILL_DIR}/references/entities-value-objects.md") 以获取完整模式。
from dataclasses import dataclass
from decimal import Decimal
@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)
加载 Read("${CLAUDE_SKILL_DIR}/references/entities-value-objects.md") 以获取地址、日期范围等示例。
| 决策 | 建议 |
|---|---|
| 实体 vs 值对象 | 具有唯一 ID + 生命周期?选实体。否则选值对象 |
| 实体相等性 | 通过 ID 判断,而非属性 |
| 值对象可变性 | 始终不可变 (frozen=True) |
| 仓储范围 | 每个聚合根一个 |
| 领域事件 | 在实体中收集,持久化后发布 |
| 上下文边界 | 按业务能力划分,而非技术 |
| 规则 | 影响 | 涵盖内容 |
|---|---|---|
aggregate-boundaries (加载 ${CLAUDE_SKILL_DIR}/rules/aggregate-boundaries.md) | 高 | 聚合根设计、通过 ID 引用、每次事务一个 |
aggregate-invariants (加载 ${CLAUDE_SKILL_DIR}/rules/aggregate-invariants.md) | 高 | 业务规则强制执行、规约模式 |
aggregate-sizing (加载 ${CLAUDE_SKILL_DIR}/rules/aggregate-sizing.md) | 高 | 合理确定大小、何时拆分、最终一致性 |
实体少于 5 个?完全跳过 DDD。其繁文缛节的成本超过了收益。
| 模式 | 面试 | 黑客松 | 最小可行产品 | 增长期 | 企业级 | 更简单的替代方案 |
|---|---|---|---|---|---|---|
| 聚合 | 过度设计 | 过度设计 | 过度设计 | 选择性使用 | 适用 | 带有验证的普通数据类 |
| 有界上下文 | 过度设计 | 过度设计 | 过度设计 | 临界点 | 适用 | 具有清晰导入的 Python 包 |
| CQRS | 过度设计 | 过度设计 | 过度设计 | 过度设计 | 有理由时使用 | 读写使用单一模型 |
| 值对象 | 过度设计 | 过度设计 | 临界点 | 适用 | 必需 | 实体上的类型化字段 |
| 领域事件 | 过度设计 | 过度设计 | 过度设计 | 选择性使用 | 适用 | 服务间的直接方法调用 |
| 仓储模式 | 过度设计 | 过度设计 | 临界点 | 适用 | 必需 | 服务层中的直接 ORM 查询 |
经验法则: DDD 会增加约 40% 的代码开销。仅当领域复杂性确实需要时才值得使用(5 个以上实体且不变式跨越多个对象)。使用 DDD 的 CRUD 应用是一个危险信号。
# 绝不要有贫血领域模型(仅包含数据的类)
@dataclass
class Order:
id: UUID
items: list # 错误 - 没有行为!
# 绝不要将基础设施泄漏到领域
class Order:
def save(self, session: Session): # 错误 - 了解数据库!
# 绝不要使用可变的值对象
@dataclass # 错误 - 缺少 frozen=True
class Money:
amount: Decimal
# 绝不要让仓储返回 ORM 模型
async def get(self, id: UUID) -> OrderModel: # 错误 - 应返回领域对象!
aggregate-patterns - 聚合设计的深入探讨ork:distributed-systems - 跨聚合协调ork:database-patterns - DDD 的数据库模式设计使用 Read("${CLAUDE_SKILL_DIR}/references/<file>") 按需加载:
| 文件 | 内容 |
|---|---|
entities-value-objects.md | 完整的实体和值对象模式 |
repositories.md | 仓储模式实现 |
domain-events.md | 事件收集与发布 |
bounded-contexts.md | 上下文映射和防腐层 |
关键词: entity, identity, lifecycle, mutable, domain object 解决的问题: 在 Python 中建模实体、标识相等性、添加行为
关键词: value object, immutable, frozen, dataclass, structural equality 解决的问题: 创建不可变的值对象、何时使用值对象 vs 实体
关键词: domain service, business logic, cross-aggregate, stateless 解决的问题: 何时使用领域服务、跨越聚合的逻辑
关键词: repository, persistence, collection, IRepository, protocol 解决的问题: 实现仓储模式、抽象数据库访问、ORM 映射
关键词: bounded context, context map, ACL, subdomain, ubiquitous language 解决的问题: 定义有界上下文、与防腐层集成、上下文关系
每周安装次数
157
代码仓库
GitHub 星标数
132
首次出现
Jan 22, 2026
安全审计
安装于
opencode143
gemini-cli142
codex138
github-copilot138
cursor127
amp124
Model complex business domains with entities, value objects, and bounded contexts.
┌─────────────────────────────────────────────────────────────┐
│ 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/ │
└─────────────────────────────────────────────────────────────┘
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.
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.
| 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 |
| 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 |
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 |
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.
# 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!
aggregate-patterns - Deep dive on aggregate designork:distributed-systems - Cross-aggregate coordinationork:database-patterns - Schema design for DDDLoad 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 |
Keywords: entity, identity, lifecycle, mutable, domain object Solves: Model entities in Python, identity equality, adding behavior
Keywords: value object, immutable, frozen, dataclass, structural equality Solves: Create immutable value objects, when to use VO vs entity
Keywords: domain service, business logic, cross-aggregate, stateless Solves: When to use domain service, logic spanning aggregates
Keywords: repository, persistence, collection, IRepository, protocol Solves: Implement repository pattern, abstract DB access, ORM mapping
Keywords: bounded context, context map, ACL, subdomain, ubiquitous language Solves: Define bounded contexts, integrate with ACL, context relationships
Weekly Installs
157
Repository
GitHub Stars
132
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode143
gemini-cli142
codex138
github-copilot138
cursor127
amp124
站立会议模板:敏捷开发每日站会指南与工具(含远程团队异步模板)
10,500 周安装
| 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 |