主动应用于设计API、微服务或可扩展后端结构时。触发条件:DDD、整洁架构、六边形架构、端口与适配器、实体、值对象、领域事件、CQRS、事件溯源、仓储模式、用例、洋葱架构、发件箱模式、聚合根、防腐层。适用于领域模型、聚合、仓储或限界上下文。整洁架构 + DDD + 六边形模式用于后端服务,语言无关(Go、Rust、Python、TypeScript、Java、C#)。
整洁架构 + DDD + 六边形架构
结合DDD战术模式、整洁架构依赖规则和六边形端口/适配器的后端架构,用于构建可维护、可测试的系统。
本技能是多种相关架构传统的观点性综合,并非单一规范架构模型。请根据设计问题选择对应的原始来源:领域建模用DDD,端口/适配器用六边形架构,依赖方向用整洁架构,领域中心分层用洋葱架构,CQRS/事件溯源仅用于特定的读/写或时间需求。
何时使用(及何时不使用)
| 使用场景 | 跳过场景 |
|---|---|
| 复杂业务领域,规则繁多 | 简单CRUD,业务规则少 |
| 长期维护的系统(数年) | 原型、MVP、一次性代码 |
| 5人以上团队 | 单人开发或小团队(1-2人) |
| 多个入口点(API、CLI、事件) | 单一入口点,简单API |
| 需要替换基础设施(数据库、消息代理) | 固定基础设施,不易变更 |
| 需要高测试覆盖率 | 快速脚本、内部工具 |
从简单开始,仅在需要时增加复杂度。 大多数系统不需要完整的CQRS或事件溯源。
模式边界
| 模式 | 主要问题 | 用途 | 不要视为 |
|---|---|---|---|
| DDD | 如何建模复杂业务领域? | 通用语言、限界上下文、聚合、值对象 | 仅文件夹结构 |
| 六边形架构 | 应用程序如何与外部世界交互? | 端口、驱动适配器、被驱动适配器、可测试的应用核心 | 强制六边或精确包布局 |
| 整洁架构 | 依赖应指向哪个方向? | 向内依赖规则、用例边界、框架无关性 | 通用四文件夹模板 |
| 洋葱架构 | 如何保持领域模型为中心? | 以领域为中心的分层和依赖反转 | 当整洁/六边形已解决问题时的额外要求 |
| CQRS | 读和写是否需要不同模型? | 读写负载差异大的限界上下文 | 默认应用架构 |
| 事件溯源 | 是否需要完整事件历史的状态? | 审计、时间查询、可重放工作流 | CRUD系统的持久化默认 |
关键:依赖规则
依赖仅向内指向。外层依赖内层,反之则不允许。
基础设施 → 应用层 → 领域层
(适配器) (用例) (核心)
需捕获的违规:
- 领域层导入数据库/HTTP库
- 在此架构风格中,控制器直接调用仓储而非应用用例
- 实体依赖应用服务
设计验证: "让你的应用程序在没有UI或数据库的情况下也能工作" — Alistair Cockburn。如果你能在没有基础设施的情况下从测试中运行领域逻辑,那么边界就是正确的。
快速决策树
"这段代码放哪里?"
放哪里?
├─ 纯业务逻辑,无I/O → domain/
├─ 编排领域 + 有副作用 → application/
├─ 与外部系统通信 → infrastructure/
├─ 定义如何交互(接口) → port(领域或应用层)
└─ 实现端口 → adapter(基础设施)
易错点 — LLM最常放错的位置:
| 代码 | 层 | 原因 |
|---|---|---|
| 业务不变量("订单需要商品才能确认") | 领域(实体方法) | 这是规则,不是编排 |
| 输入格式验证(JSON结构、必填字段) | 适配器(控制器/DTO) | 协议关注点,非业务规则 |
| 事务开始/提交 | 应用层 | 用例 = 事务边界 |
| ORM实体/表模型 | 基础设施 | 映射到领域对象;绝不让ORM实体成为领域实体 |
| 领域↔DB映射 | 基础设施(映射器) | 持久化细节 |
| 授权("用户是否允许?") | 应用层(策略)或适配器中间件 | 领域保持与授权无关;仅当角色规则是业务规则时才在领域层编码 |
| 时钟、UUID生成 | 领域/应用层端口;基础设施适配器 | 保持领域确定性和可测试性 |
| 响应领域事件 | 应用层(事件处理器) | 副作用 = 编排 |
| 为屏幕查询多表连接 | 读模型(应用层接口,基础设施实现) | 不要强制通过聚合 |
贫血领域模型的试金石: 如果应用服务从实体读取状态、做出决策、然后写回状态(if (order.status === 'draft') order.status = 'confirmed'),将该逻辑移到实体中作为 order.confirm()。处理器应像脚本一样:加载聚合 → 调用一个行为方法 → 保存 → 发布。
"这是实体还是值对象?"
实体还是值对象?
├─ 具有持久化的唯一标识 → 实体
├─ 仅由其属性定义 → 值对象
├─ "这是同一个东西吗?" → 实体(身份比较)
└─ "具有相同的值吗?" → 值对象(结构相等)
"这应该是一个独立的聚合吗?"
聚合边界?
├─ 必须在同一事务中保持一致 → 同一聚合
├─ 可以最终一致 → 独立聚合
├─ 仅通过ID引用 → 独立聚合
└─ 聚合中实体超过10个 → 拆分
规则: 每个事务一个聚合。跨聚合一致性通过领域事件(最终一致性)实现。
目录结构
src/
├── domain/ # 核心业务逻辑(无外部依赖)
│ ├── {aggregate}/
│ │ ├── entity # 聚合根 + 子实体
│ │ ├── value_objects # 不可变值类型
│ │ ├── events # 领域事件
│ │ ├── repository # DDD仓储接口(被驱动端口)
│ │ └── services # 领域服务(无状态逻辑)
│ └── shared/
│ └── errors # 领域错误
├── application/ # 用例 / 应用服务
│ ├── {use-case}/
│ │ ├── command # 命令/查询DTO
│ │ ├── handler # 用例实现
│ │ └── port # 驱动端口接口
│ └── shared/
│ └── unit_of_work # 事务抽象
├── infrastructure/ # 适配器(外部关注点)
│ ├── persistence/ # 数据库适配器
│ ├── messaging/ # 消息代理适配器
│ ├── http/ # REST/GraphQL适配器(驱动)
│ └── config/
│ └── di # 依赖注入 / 组合根
└── main # 引导 / 入口点
端口放置: 本技能默认采用以DDD为中心的布局,聚合仓储接口位于 domain/ 中的聚合旁边。更严格的六边形布局可能将被驱动端口放在 application/ports/driven/ 下。每个代码库选择一种约定,并保持依赖规则完整。
表示层: 驱动适配器(REST/gRPC/CLI)默认位于 infrastructure/ 下。某些代码库将其提升为第四层 presentation/(references/LAYERS.md 展示了该变体)。为控制器选择一个位置,不要同时使用。
事件发布: 保存聚合然后将其事件发布到代理是两次写入;两者之间的崩溃会静默丢弃事件。当事件必须可靠地到达其他服务时,在与聚合相同的事务中将事件写入发件箱表 — 参见 references/CQRS-EVENTS.md 中的发件箱模式。
DDD构建块
| 模式 | 目的 | 层 | 关键规则 |
|---|---|---|---|
| 实体 | 身份 + 行为 | 领域 | 按ID相等 |
| 值对象 | 不可变数据 | 领域 | 按值相等,无setter |
| 聚合 | 一致性边界 | 领域 | 仅根被外部引用 |
| 领域事件 | 变更记录 | 领域 | 过去时命名(OrderPlaced) |
| 仓储 | 持久化抽象 | 领域(端口) | 每个聚合一个,非每个表一个 |
| 领域服务 | 无状态逻辑 | 领域 | 当逻辑不适合实体时 |
| 应用服务 | 编排 | 应用层 | 协调领域 + 基础设施 |
反模式(关键)
| 反模式 | 问题 | 修复 |
|---|---|---|
| 贫血领域模型 | 实体是数据容器,逻辑在服务中 | 将行为移入实体 |
| 每个实体一个仓储 | 破坏聚合边界 | 每个聚合一个仓储 |
| 泄露基础设施 | 领域导入DB/HTTP库 | 领域零外部依赖 |
| 上帝聚合 | 实体过多,事务慢 | 拆分为更小的聚合 |
| 跳过用例 | 控制器直接调用仓储 | 通过应用用例路由 |
| CRUD思维 | 建模数据而非行为 | 建模业务操作 |
| 过早CQRS | 在需要前增加复杂度 | 从简单读写开始,逐步演进 |
| 跨聚合事务 | 一个事务中多个聚合 | 使用领域事件实现一致性 |
实现顺序
- 发现领域 — 事件风暴、与领域专家对话
- 建模领域 — 实体、值对象、聚合(无基础设施)
- 定义端口 — 仓储接口、外部服务接口
- 实现用例 — 协调领域的应用服务
- 最后添加适配器 — HTTP、数据库、消息实现
DDD是协作的。 与领域专家的建模会话与代码模式同等重要。
参考文档
在执行左侧列出的任务前,阅读对应的文件:
| 在您... | 阅读 |
|---|---|
| 在任何层编写代码、注入依赖、或决定3层vs4层 | references/LAYERS.md |
| 将系统拆分为服务/上下文、与遗留或第三方系统集成(ACL)、运行事件风暴 | references/DDD-STRATEGIC.md |
| 建模实体、值对象、聚合、仓储、领域服务或工厂 | references/DDD-TACTICAL.md |
| 定义端口/适配器、命名接口、或布局端口优先结构 | references/HEXAGONAL.md |
| 添加命令/查询、领域vs集成事件、发件箱、Saga、或评估CQRS/事件溯源 | references/CQRS-EVENTS.md |
| 为任何层编写单元/集成/架构测试 | references/TESTING.md |
| 快速回答"哪个模式/哪一层"问题而不深入 | references/CHEATSHEET.md |
来源
主要来源
- 六边形架构 — Alistair Cockburn (2005)
- 领域驱动设计:蓝皮书 — Eric Evans (2003)
- 整洁架构 — Robert C. Martin (2012)
- 洋葱架构 — Jeffrey Palermo (2008)
- 实现领域驱动设计 — Vaughn Vernon (2013)
主要模式参考
- CQRS — Martin Fowler
- 事件溯源 — Martin Fowler
- 仓储模式 — Martin Fowler (PoEAA)
- 工作单元 — Martin Fowler (PoEAA)
- 限界上下文 — Martin Fowler
- 事务性发件箱 — microservices.io
- 有效聚合设计 — Vaughn Vernon
实现指南
- 微软:DDD + CQRS微服务
- 领域事件 — Udi Dahan
补充综合
- 整洁架构:站在巨人肩膀上 — Herberto Graça
- 显式架构 — Herberto Graça(观点性综合,非规范来源)
- 动手实践整洁架构 — Tom Hombergs






