DDD微服务代码模型设计
# DDD 微服务代码模型设计
领域模型建好了,代码目录该怎么组织?这篇合并整理代码模型上下两讲——从目录结构到"领域对象怎么映射成代码对象"。
# 一、四个一级目录
代码模型直接按 DDD 四层架构切一级目录:
microservice/
├── interfaces/ # 用户接口层
├── application/ # 应用层
├── domain/ # 领域层
└── infrastructure/ # 基础层
1
2
3
4
5
2
3
4
5
# 二、各层内部结构
# interfaces(用户接口层)
interfaces/
├── assembler/ # DTO ↔ 领域对象 互转
├── dto/ # 数据传输对象,纯数据无逻辑,隔离内部领域对象
└── facade/ # 粗粒度接口,把请求委派给应用服务
1
2
3
4
2
3
4
# application(应用层)
application/
├── event/
│ ├── publish/ # 事件发布
│ └── subscribe/ # 事件订阅
└── service/ # 应用服务:组合编排领域服务
1
2
3
4
5
2
3
4
5
一个建议:微服务内所有事件的发布和订阅统一收口在应用层,事件相关的核心业务逻辑放领域层——统一管理,不散落。
# domain(领域层)—— 核心
领域层按聚合分包,每个聚合内部结构统一:
domain/
├── aggregate-a/ # 聚合包(按实际聚合命名,如 person/)
│ ├── entity/ # 聚合根、实体、值对象、工厂
│ ├── event/ # 事件实体及处理逻辑
│ ├── service/ # 领域服务
│ └── repository/ # 仓储接口 + 仓储实现
└── aggregate-b/
└── ...
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
两个刻意为之的设计:
- 按聚合分包:聚合边界即包边界。将来微服务要拆分重组,以聚合包为单位整体搬迁即可
- 仓储实现也放聚合包里:按分层原则仓储实现该在基础层,但为了聚合能"整包迁移"(业务逻辑 + 持久化一起走),故意放进聚合包——架构演进的便利性优先于分层的纯粹性
# infrastructure(基础层)
infrastructure/
├── config/ # 配置
└── util/ # 数据库、缓存、MQ、网关、三方库等,按资源类别建子目录
1
2
3
2
3
# 三、领域对象 → 代码对象的映射
事件风暴产出的是业务视角的领域对象(聚合、实体、命令、事件),落代码前要做一轮微服务设计,把它们细化并归位。用表格记录映射关系:层、领域对象、领域类型、对应代码类、所在包——这张表就是"业务模型和代码模型一致性"的保证。
各类对象的设计要点(以个人客户聚合为例):
| 领域对象 | 设计要点 | 代码位置 |
|---|---|---|
| 实体 | 地址、电话、银行账号这类被聚合根引用的实体,建模时容易漏,设计阶段要补出来;充血模型 | entity/ |
| 聚合根 | 个人客户实体,管理聚合内对象生命周期,通过工厂+仓储完成初始化和持久化 | entity/ |
| 值对象 | 证件类型这类枚举属性;判断标准:整体替换→值对象,要查询统计→实体 | entity/ |
| 领域事件 | 先判断发生在微服务内还是微服务间,再定是否引入事件总线/MQ | 实体在 domain/event/,发布订阅在 application/event/ |
| 领域服务 | 跨实体的业务逻辑 | domain/service/ |
| 仓储 | 一个聚合一个仓储,接口+实现,依赖倒置 | repository/ |
# 四、服务的逐层封装
严格分层下不允许跨层调用,服务从下到上:实体方法 → 领域服务 → 应用服务。
那实体方法想暴露给前端怎么办?逐层封装:
实体方法 createPerson()
→ 封装为领域服务 createPersonDomainService
→ 封装为应用服务 createPersonAppService
→ facade 暴露给前端
1
2
3
4
2
3
4
命名保持一致,用 DomainService / AppService 后缀区分层次。
看起来繁琐,但换来的是依赖关系永远清晰。而且有个附带收益:当发现多个应用服务反复用同样的方式编排同几个领域服务时,就是信号——把这几个领域服务合并下沉,领域模型就这样越用越精炼。
# 五、两条铁律
- 聚合之间的代码边界必须清晰。聚合间不允许直接调用领域服务,必须走应用层组合。这是未来"以聚合为单位重组微服务"的前提
- 写代码时刻问自己"这段逻辑属于哪层"。核心领域逻辑跑进应用层,DDD 分层慢慢就会退化成三层架构——这是最常见的失败方式
# 六、一次请求的完整流转(帮助理解)
以"创建客户"为例串一遍:
前端 POST /customers
→ interfaces: DTO 接收 → Assembler 转领域对象 → Facade 调应用服务
→ application: 应用服务编排(调领域服务、控制事务、发布"客户已创建"事件)
→ domain: 领域服务 → 聚合根方法执行业务逻辑 → 仓储接口持久化
→ infrastructure: 仓储实现落库
1
2
3
4
5
2
3
4
5
每层只做自己的事,这就是代码模型的价值。
编辑 (opens new window)
上次更新: 2026/10/08, 03:22:29