DDD 领域驱动设计 · 深入
领域驱动设计:从战略到战术,再到代码
DDD 不是又一套分层架构模板,而是一套两段式方法:先发现业务(哪里是核心、词在哪个边界里换了意思),再设计模型(聚合多大、哪条边界事务一致、哪条最终一致)。这份教程用同一个「云市」电商交易平台贯穿十章,把这套方法逐层落到 Java 代码。
·适合谁 / 不适合谁 / 读完之后你能做到什么
这份教程默认你已经写过常规分层(Controller / Service / DAO),听过 DDD 的名词,但还画不出边界、说不清聚合该多大。它的目标不是堆定义,而是重写你的几条旧直觉:一个企业级统一数据模型、对象图等于一个事务、Service 层就是业务逻辑、一张表对应一个聚合、一个限界上下文对应一个微服务。
- 适合 · 想搞懂边界与聚合的工程师
会常规分层、能读写 Java,遇到过"这块逻辑该放哪一层""这两个对象要不要放进一个事务""一个Product类为什么越长越乱"这类问题,想要一套能反复套用的判定,而不是又一篇名词解释。 - 不适合 · 找 Spring / JPA API 教程的人
这里不教注解怎么配、仓储怎么接 Hibernate、事务注解放哪。那是框架手册的事,去看 Spring Boot / Spring 核心 教程;本教程只讲模型,不讲持久化框架的 API。 - 不适合 · 纯 CRUD、无领域复杂度的项目
如果你的系统就是几张表增删改查、规则薄到一眼看穿,DDD 的聚合、限界上下文、领域事件全是净成本。08 章会专门讲"何时不该用 DDD",对这类项目,结论就是退回 CRUD。 - 读完之后你能做到什么
给一段业务描述,画出它的限界上下文与上下文映射、勾出每个聚合的边界;判断一条规则该走事务一致还是最终一致;一眼识别贫血模型(Anemic Domain Model);并判断一个项目何时不该上 DDD。
书和官方文档会告诉你"聚合是一致性边界"。本教程再往下一层:聚合的大小不由表关联决定,由"真正的不变量(true invariant)"决定——因为聚合是乐观锁的版本号单位,做大了就是并发争用的热点。同理,事务一致还是最终一致,是个组织问题(发命令的人是否必须立刻看到结果),不是基础设施开关。这两条判定,是 5 年经验也常踩错、而 API 文档永远不会讲的地方。
·一句话本质
下面五条是全书反复回扣的判定基线。每一条都在重写一个常见误解,请先读一遍,后面每章会拿「云市」的具体情形把它走实。
① 限界上下文是语义边界,不是微服务边界。
同一个"商品",在目录(Catalog)里是带图集营销文案的胖展示对象,在销售(Sales)里是价格名称都被冻结的下单快照,在库存(Inventory)里只是 SKU 加可用数量。一个企业级统一 Product 类会长出几十个互相矛盾的字段。结论:不存在统一模型,只能在上下文的缝隙处显式翻译。
② 聚合是事务 / 一致性边界,不是 has-a 对象图。
"一个事务一个聚合"源自乐观锁:聚合是版本号的单位。所以聚合该多大,由它必须时刻守住的真正不变量决定(如订单总额必须等于各订单项之和),不由数据库表之间有没有外键决定。把"能 join 到的全塞进一个聚合"是最常见的设计错误。
③ 事务一致 vs 最终一致是个组织问题,不是基础设施开关。
判定只有一句:发出这条命令的人,是否必须在同一瞬间看到结果。买家删一个订单项要立刻看到金额变化 → 同一聚合、事务一致;买家不必在下单那一刻看到仓库扣减库存 → 跨聚合、最终一致。领域事件(Domain Event)就是缝隙处的信使,它可以在进程内传递,不等于必须上 Kafka。
④ DDD 说"什么",六边形 / 整洁架构说"怎么"。
最常见的混淆是把领域服务(Domain Service)和分层里的 Service 层划等号。领域服务装真实领域逻辑(如一笔订单的定价算法 PricingService),名字进入统一语言;应用服务(Application Service)只做编排——加载、调用、保存、发事件,零业务规则。分不清这一对,DDD 就退化成换了壳的三层架构。
⑤ 均匀地用 DDD 最贵。
核心域(core domain)自己研发、通用域(generic)直接买、支撑域(supporting)简单 CRUD 即可。限界上下文不等于微服务,模块化单体优先,服务边界应当从领域边界里涌现,而不是一开始就拆成一堆进程。把战术构件均匀地刷满整个系统,是把成本花在不产生差异的地方。
·概念地图
下面这张图是全书的骨架,竖向分三层:上层战略设计找边界,下层战术构件守边界,中间用朱红高亮的事件风暴(Event Storming)把两层接起来——它是把战略边界落成战术聚合的那座桥。最底下是承载这些模型的架构选择。
·现状速览
· 稳定:事件风暴;聚合四原则(真正不变量 / 小聚合 / 按标识引用 / 边界外最终一致);问题空间 vs 解空间的拆分框架;函数式 DDD 的"让非法状态无法表示"(思想已从 F# 扩散到 TypeScript / Rust / Kotlin)。这些是十余年沉淀下来、可放心当地基学的部分。
· 流变中:DDD 默认捆绑 CQRS + 事件溯源的旧习惯正被社区主动解耦,改为按上下文、仅核心域投入;用 LLM 辅助建模、用限界上下文给 AI 智能体划上下文窗口等做法处于探索期,尚未被证实。
· 被推翻:「一个限界上下文 = 一个微服务」的硬规则(当前共识是模块化单体优先,服务从边界涌现);用 Kafka 当事件存储(它缺按实体分流与乐观并发,不适合做事件溯源的存储)。这两条若按旧资料照搬,是当下最典型的失败模式。
·关于"读得顺"的警告
DDD 的内容尤其容易制造"我懂了"的错觉:名词面熟、例子直白、读起来毫不费力。但建模能力不是读出来的,是在含糊的业务描述前画错、被驳、重画练出来的。下面三句心里话一旦冒出来,恰恰说明你还没学进去——这是流畅性错觉:
· 「我读得很顺」——顺,往往是因为词面熟,不是因为真能复述机制。合上页面,能讲清"聚合该多大由什么决定"吗?
· 「我做题很快」——题答得快,往往是因为碰到的是背过的标准题型;把同一判定换个业务壳,就未必答得动。
· 「我没卡壳」——没卡壳,往往是因为你只在看字,没在脑子里拿「云市」真跑一遍边界与事务。每章末的自测、09 章的综合演练,就是用来戳破这层错觉的,别跳过。
·怎么读这份教程
九章按"战略 → 桥 → 战术 → 架构 → 自检"递进,全部用同一个「云市」示例,后面的章节会反复提取前面建立的概念。建议顺序读,02→03→04→05 是主干,别跳。
·相关教程
DDD 与同库其他主题互为前后置:先有设计模式的对象建模直觉,DDD 的战术构件才好理解;DDD 划出的边界与领域事件,又落在分布式系统与 Spring 的工程实现上。