本篇要搞懂的 3 件事:DDD 分层架构长什么样(四层各干啥)、它最重要的原则(每层只依赖下一层 + 依赖倒置)、它怎么从传统三层架构演进过来。
承接前文:04~06 讲完了领域模型里的"零件和规则"(实体、值对象、聚合、领域事件)。但这些东西在代码工程里放哪一层、谁能调谁,还没说。07 讲就是给前面所有概念安排工位——这就是 DDD 分层架构。
04 实体/值对象 ─┐
05 聚合/聚合根 ─┼─→ 这些领域对象,在代码里统统住在「领域层」
06 领域事件 ─┘ ↑ 07 讲:四层架构给每个概念安排好工位和调用规则
DDD 分层架构长什么样
它是"优化后的四层架构"。从上到下四层,上层依赖下层:
┌────────────────────────────────────────────┐
│ 用户接口层 (User Interface) │ 对外的脸:收请求、返数据(DTO)
├────────────────────────────────────────────┤
│ 应用层 (Application) │ 调度员:编排聚合、控事务、发/订阅事件(薄,不写业务规则)
├────────────────────────────────────────────┤
│ 领域层 (Domain) ★核心★ │ 业务大脑:聚合根/实体/值对象/领域服务,核心业务逻辑全在这
├────────────────────────────────────────────┤
│ 基础层 (Infrastructure) │ 后勤:数据库/缓存/消息中间件/第三方工具
└────────────────────────────────────────────┘
↑ 依赖倒置(DIP):上层不直接依赖基础层实现,靠接口解耦
原文架构图(领域层里装着聚合 / 实体 / 值对象 / 领域服务,基础层贯穿右侧、服务所有层):
四层各自的职责
| 层 | 一句话定位 | 干什么 | 关键提醒 |
|---|---|---|---|
| 用户接口层 | 对外的脸 | 向"用户"展示信息、解释指令。用户可以是人、程序、自动化测试、批处理脚本。引入 DTO 给前端更灵活的数据 | — |
| 应用层 | 调度员(很薄) | 编排/组合多个聚合的服务、控制用例执行顺序、拼装结果;还负责安全认证、权限、事务控制、发/订阅领域事件;是微服务之间交互的通道 | 绝不能把业务规则写这层! 否则领域模型失焦,时间一长退化回传统三层架构 |
| 领域层 ★ | 业务大脑(真正的核心) | 实现企业核心业务逻辑,表达业务概念、状态、规则。装着聚合根、实体、值对象、领域服务 | 业务逻辑主要由实体(充血模型)和领域服务实现 |
| 基础层 | 后勤(贯穿所有层) | 提供通用技术服务:数据库持久化、缓存、消息中间件、网关、第三方工具等 | 用依赖倒置封装,换数据库时只改这层,不动上层业务 |
🪞 辅助理解(开餐厅):
用户接口层 = 服务员(点单、上菜,跟客人打交道);
应用层 = 传菜/调度(喊单、安排顺序,但自己不炒菜);
领域层 = 后厨大厨(真正做菜、决定怎么做,核心手艺在这);
基础层 = 水电煤、冰箱、灶具(供给所有人用的基础设施,换个灶台不影响菜谱)。
应用层 vs 领域层:到底怎么分?(最难、最关键的一刀)
这是 DDD 分层里最容易糊的地方——两层好像都在"处理业务"。记住一句话就够了:
领域层管"怎样做才是对的"(业务规则、对错判断);应用层管"按什么步骤把这件事办完"(流程编排,自己不做对错判断)。
应用层像调度员 / 包工头:不亲自干活,只负责"先叫谁、再叫谁、活干完通知谁、出错怎么收场"。领域层像真正的工匠:知道每件活的门道和规矩。
用"下单"走一遍,看两层各写什么:
应用层 PlaceOrderAppService.placeOrder(请求) ← 只编排,不含业务规则
1. 开启事务 / 校验登录权限
2. 调 用户聚合:查这个用户能不能下单
3. 调 订单.create(...) ← 把"造订单"这件事甩给领域层
4. 调 库存.deduct(...) ← 把"扣库存"这件事甩给领域层
5. 发布领域事件「订单已创建」
6. 提交事务 / 出错则回滚
↑ 它从头到尾没判断过"金额对不对""库存够不够",只是按顺序喊话 + 控事务
领域层 Order.create(...) ← 业务规则在这
· 校验:订单总金额 == 各明细(单价×数量)之和 ← 算错就是 bug
· 设置初始状态 = 待支付,生成订单号
Inventory.deduct(数量)
· 判断:库存 >= 数量,否则抛"库存不足"异常 ← 这就是业务对错
· 扣减库存
关键:第 3、4 步应用层只是"把活派出去",“造订单要满足什么规则”"库存够不够能不能扣"全由领域层说了算。应用层负责的是事务、权限、顺序、发事件这些流程性的事——换个前端、换个用例,顺序可能变,但业务规则不变。
分不清时的万能判断法:
把这段逻辑删掉会发生什么?
- 会算错账、违反业务规则(钱算错、库存扣成负数)→ 属于领域层
- 只是少走一步流程(没发通知、没记日志、顺序乱了)→ 属于应用层
⚠️ 原文反复强调的坑:别把业务规则写进应用层。一旦应用层开始写"if 金额 > xxx 则打折"这种规则,领域层就被架空了,时间一长就退化回"Service 里一坨"的传统三层架构。
| 应用层 | 领域层 | |
|---|---|---|
| 回答的问题 | “这件事分几步、谁先谁后” | “这件事怎样算对” |
| 含业务规则吗 | 不含(含了就是设计跑偏) | 全部业务规则都在这 |
| 有状态吗 | 无状态,很薄 | 有状态(实体/聚合),很厚 |
| 典型职责 | 事务、权限、编排多个聚合、发/订阅事件 | 计算、校验不变量、状态变更、领域服务 |
| 换个前端/用例会变吗 | 会(流程随用例变) | 不会(规则是业务本质) |
实体 vs 领域服务:谁实现业务逻辑?(领域层内部的细分)
这是设计领域层时最容易含糊的点:
| 实体(充血模型) | 领域服务 | |
|---|---|---|
| 管什么 | 与自己相关的业务逻辑 | 单个实体搞不定、要组合多个实体/值对象的复杂逻辑 |
| 类比 | 一个员工干自己分内的活 | 需要几个员工协作时,出来个"项目协调人" |
→ 不是同级关系:能单实体搞定就别上领域服务;只有跨多个实体/值对象的逻辑,领域服务才出马。
最重要的原则:每层只依赖它正下方那一层
DDD 分层架构有个铁律:每层只能与位于其直接下方的层耦合。 按耦合松紧分两种:
| 架构 | 规则 | 评价 |
|---|---|---|
| 严格分层架构(优化后 DDD 用这个,推荐) | 任何层只能依赖直接下方那一层。领域服务只能被应用服务调,应用服务只能被用户接口层调 | 依赖关系清晰、可管理 |
| 松散分层架构(传统 DDD) | 某层可依赖任意下方的层(用户接口层能直接调领域服务) | 依赖混乱、难管理,核心业务逻辑容易外泄 |
为什么选严格分层? 试想领域层某个服务大改了,怎么通知所有调用方?严格分层里只需逐层通知上一层;松散分层里调用方四面八方,根本理不清。
依赖倒置(DIP):为什么基础层在最下面却不是核心
传统四层架构里基础层(数据库)被所有层依赖、位于最核心——但软件真正的核心是领域层(业务),不是数据库。这个依赖方向是反的。
依赖倒置就是把它扭正:
- 仓储接口(Repository 接口)放在领域层——领域层只认"我要存取数据"这个抽象。
- 仓储实现放在基础层——具体用 MySQL 还是别的,是实现细节。
🪞 辅助理解:领域层说"给我一个能存订单的柜子就行,我不管你柜子什么牌子"(接口);基础层负责造具体的柜子(实现)。换数据库 = 换个柜子,菜谱(业务逻辑)一个字不用改。这就是为什么很多公司最怕换数据库,而 DDD 用依赖倒置把这个痛点根除了。
看左右对比:左边传统四层,基础层在最底被所有层依赖(错把数据库当核心);右边依赖倒置后,领域层沉到最底成为核心,基础层反而依赖它:
DDD 分层架构如何推动架构演进
分层 + 清晰边界带来的好处:领域模型变了,微服务能跟着平滑演进。
1. 微服务级演进——以"聚合"为搬迁单元
领域对象从内到外:值对象 → 实体 → 聚合 → 限界上下文。实体/值对象的小改不影响大局,但聚合的拆分/重组会改变微服务边界。 所以拿聚合当演进的基本单元:
- 聚合 a 被高频访问、拖累整个微服务1 → 把 a 剥离出来独立成微服务2,单独应对高并发。
- 发现聚合 d 更适合放微服务1 → 把 d 整体搬迁过来。
- 结果:微服务1 从 {a,b,c} 演进成 {b,c,d}。
前提:设计时就定义好聚合之间清晰的代码边界,搬迁才轻松。
2. 微服务内演进——领域服务的"下沉"
设计初期领域层只提供原子服务(领域服务 a、b、c)。随着接入变多,发现领域服务 b、c 总是被多个应用服务一起、按相同顺序调用 → 把 b、c 合并、把这段编排逻辑从应用层下沉到领域层,演进成新领域服务 (b+c)。
→ 效果:服务数量减少,上层编排变简单,领域模型越来越精炼。
三层架构 → DDD 分层架构
传统单体大多是三层架构(表现层 / 业务逻辑层 / 数据访问层),是逻辑分层、物理上集中式,不适合分布式微服务。演进时主要动两处:
| 三层架构 | DDD 分层架构 | 变化 |
|---|---|---|
| 表现层 | 用户接口层 | 引入 DTO,前端取数更灵活 |
| 业务逻辑层 | 拆成应用层 + 领域层 | 应用层快速响应前端变化,领域层沉淀领域模型能力——解决了三层里"核心逻辑混乱、改动互相影响"的老problem |
| 数据访问层(DAO) | 基础层(Repository 仓储) | 从 DAO 换成仓储模式 + 依赖倒置:仓储接口在领域层、实现在基础层;通用工具/Config 统一进基础层 |
核心思想一句话:要素没变,只是被重新归类、重新划层,并明确了层间的交互规则和职责边界。
总结
DDD 分层架构是微服务的核心框架,四层从上到下是用户接口层、应用层、领域层、基础层:用户接口层对外、应用层做薄薄的编排调度、领域层是真正的业务核心(装着 04~05 讲的所有领域对象)、基础层提供技术后勤。
它最重要的原则是严格分层(每层只依赖正下方一层,依赖清晰可管理)+ 依赖倒置(仓储接口在领域层、实现在基础层,把"数据库是核心"这个错误的依赖方向扭正,换数据库不动业务)。
分层 + 清晰边界让架构能平滑演进:对外以聚合为单元拆分/搬迁微服务,对内让高频共用的领域服务下沉合并。从传统三层演进过来,主要是把业务逻辑层拆成应用层+领域层、把 DAO 换成仓储模式。
一句话速记
领域层是大脑、应用层是调度、接口层是脸、基础层是后勤;上只调下,数据库靠依赖倒置被踢下神坛——业务才是核心。
思考题
结合自己的业务,对号入座:
- 领域层会有哪些领域对象?(哪些聚合根、实体、值对象,哪些复杂逻辑要用领域服务)
- 应用层会有哪些?(哪些是"编排多个聚合、控事务、发事件"的调度逻辑——注意它们不该含业务规则)
- 你现在的代码里,有没有把本该在领域层的业务规则塞进了应用层 / 控制器?那就是退化回三层架构的征兆。





