加载中...

本篇要搞懂的 3 件事:DDD 分层架构长什么样(四层各干啥)、它最重要的原则(每层只依赖下一层 + 依赖倒置)、它怎么从传统三层架构演进过来。

承接前文:04~06 讲完了领域模型里的"零件和规则"(实体、值对象、聚合、领域事件)。但这些东西在代码工程里放哪一层、谁能调谁,还没说。07 讲就是给前面所有概念安排工位——这就是 DDD 分层架构。

04 实体/值对象 ─┐
05 聚合/聚合根  ─┼─→ 这些领域对象,在代码里统统住在「领域层」
06 领域事件     ─┘        ↑ 07 讲:四层架构给每个概念安排好工位和调用规则

DDD 分层架构长什么样

它是"优化后的四层架构"。从上到下四层,上层依赖下层

┌────────────────────────────────────────────┐
│ 用户接口层 (User Interface)                   │  对外的脸:收请求、返数据(DTO)
├────────────────────────────────────────────┤
│ 应用层 (Application)                          │  调度员:编排聚合、控事务、发/订阅事件(薄,不写业务规则)
├────────────────────────────────────────────┤
│ 领域层 (Domain)  ★核心★                       │  业务大脑:聚合根/实体/值对象/领域服务,核心业务逻辑全在这
├────────────────────────────────────────────┤
│ 基础层 (Infrastructure)                       │  后勤:数据库/缓存/消息中间件/第三方工具
└────────────────────────────────────────────┘
        ↑ 依赖倒置(DIP):上层不直接依赖基础层实现,靠接口解耦

原文架构图(领域层里装着聚合 / 实体 / 值对象 / 领域服务,基础层贯穿右侧、服务所有层):

DDD 四层架构图

四层各自的职责

一句话定位 干什么 关键提醒
用户接口层 对外的脸 向"用户"展示信息、解释指令。用户可以是人、程序、自动化测试、批处理脚本。引入 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 用依赖倒置把这个痛点根除了。

看左右对比:左边传统四层,基础层在最底被所有层依赖(错把数据库当核心);右边依赖倒置后,领域层沉到最底成为核心,基础层反而依赖它:

传统四层 vs 依赖倒置后的四层

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 四层架构的要素映射

核心思想一句话:要素没变,只是被重新归类、重新划层,并明确了层间的交互规则和职责边界。

总结

DDD 分层架构是微服务的核心框架,四层从上到下是用户接口层、应用层、领域层、基础层:用户接口层对外、应用层做薄薄的编排调度、领域层是真正的业务核心(装着 04~05 讲的所有领域对象)、基础层提供技术后勤。

它最重要的原则是严格分层(每层只依赖正下方一层,依赖清晰可管理)+ 依赖倒置(仓储接口在领域层、实现在基础层,把"数据库是核心"这个错误的依赖方向扭正,换数据库不动业务)。

分层 + 清晰边界让架构能平滑演进:对外以聚合为单元拆分/搬迁微服务,对内让高频共用的领域服务下沉合并。从传统三层演进过来,主要是把业务逻辑层拆成应用层+领域层、把 DAO 换成仓储模式。

一句话速记

领域层是大脑、应用层是调度、接口层是脸、基础层是后勤;上只调下,数据库靠依赖倒置被踢下神坛——业务才是核心。

思考题

结合自己的业务,对号入座:

  • 领域层会有哪些领域对象?(哪些聚合根、实体、值对象,哪些复杂逻辑要用领域服务)
  • 应用层会有哪些?(哪些是"编排多个聚合、控事务、发事件"的调度逻辑——注意它们不该含业务规则)
  • 你现在的代码里,有没有把本该在领域层的业务规则塞进了应用层 / 控制器?那就是退化回三层架构的征兆。
公告栏
这是我的个人知识库。
记录技术,也记录生活 —— 读过的、试过的、想明白的,都堆在这儿。
最新文章
网站资讯
文章数目 :
5
已运行时间 :
本站总字数 :
15.7k
本站访客数 :
本站总访问量 :
最后更新时间 :
全局知识图谱
当前页面 已访问 文章 标签
ESC 关闭 · 滚轮缩放 · 拖拽移动 · Ctrl+G 开关