DDD 领域驱动设计 · 深入

领域驱动设计:从战略到战术,再到代码

DDD 不是又一套分层架构模板,而是一套两段式方法:先发现业务(哪里是核心、词在哪个边界里换了意思),再设计模型(聚合多大、哪条边界事务一致、哪条最终一致)。这份教程用同一个「云市」电商交易平台贯穿十章,把这套方法逐层落到 Java 代码。

示例域 云市电商交易平台(Catalog / Sales / Inventory / Shipping / Payment) 代码 Java 片段,示意模型而非端到端可运行 现状 截至 2026-06

·适合谁 / 不适合谁 / 读完之后你能做到什么

这份教程默认你已经写过常规分层(Controller / Service / DAO),听过 DDD 的名词,但还画不出边界、说不清聚合该多大。它的目标不是堆定义,而是重写你的几条旧直觉:一个企业级统一数据模型、对象图等于一个事务、Service 层就是业务逻辑、一张表对应一个聚合、一个限界上下文对应一个微服务。

  • 适合 · 想搞懂边界与聚合的工程师
    会常规分层、能读写 Java,遇到过"这块逻辑该放哪一层""这两个对象要不要放进一个事务""一个 Product 类为什么越长越乱"这类问题,想要一套能反复套用的判定,而不是又一篇名词解释。
  • 不适合 · 找 Spring / JPA API 教程的人
    这里不教注解怎么配、仓储怎么接 Hibernate、事务注解放哪。那是框架手册的事,去看 Spring Boot / Spring 核心 教程;本教程只讲模型,不讲持久化框架的 API。
  • 不适合 · 纯 CRUD、无领域复杂度的项目
    如果你的系统就是几张表增删改查、规则薄到一眼看穿,DDD 的聚合、限界上下文、领域事件全是净成本。08 章会专门讲"何时不该用 DDD",对这类项目,结论就是退回 CRUD。
  • 读完之后你能做到什么
    给一段业务描述,画出它的限界上下文与上下文映射、勾出每个聚合的边界;判断一条规则该走事务一致还是最终一致;一眼识别贫血模型(Anemic Domain Model);并判断一个项目何时不该上 DDD。
5 年经验工程师能带走、官方文档给不了的一句话

书和官方文档会告诉你"聚合是一致性边界"。本教程再往下一层:聚合的大小不由表关联决定,由"真正的不变量(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)把两层接起来——它是把战略边界落成战术聚合的那座桥。最底下是承载这些模型的架构选择。

战略设计 战术构件 架构落地 领域 子域 限界上下文 语义边界 划分 落到 上下文 映射 核心 / 支撑 / 通用 事件风暴 把边界落成聚合的桥 推出命令→聚合→事件 聚合 一致性边界 仓储 / 工厂 / 领域服务 领域服务 ≠ 应用服务 领域事件 → 最终一致 发出 由真正不变量决定大小 六边形 / 整洁 CQRS / 事件溯源 何时不用 DDD 退回 CRUD 承载于
图 0全书概念地图:战略找边界、战术守边界。注意:朱红高亮的事件风暴是唯一横跨两层的节点——它是 04 章的主题,也是把 02、03 的边界落成 05 聚合的那座桥。这条朱红主线,是把 DDD 从"一堆名词"变成"一条可执行流程"的关键。

·现状速览

现状速览 · 截至 2026-06

· 稳定:事件风暴;聚合四原则(真正不变量 / 小聚合 / 按标识引用 / 边界外最终一致);问题空间 vs 解空间的拆分框架;函数式 DDD 的"让非法状态无法表示"(思想已从 F# 扩散到 TypeScript / Rust / Kotlin)。这些是十余年沉淀下来、可放心当地基学的部分。

· 流变中:DDD 默认捆绑 CQRS + 事件溯源的旧习惯正被社区主动解耦,改为按上下文、仅核心域投入;用 LLM 辅助建模、用限界上下文给 AI 智能体划上下文窗口等做法处于探索期,尚未被证实。

· 被推翻:「一个限界上下文 = 一个微服务」的硬规则(当前共识是模块化单体优先,服务从边界涌现);用 Kafka 当事件存储(它缺按实体分流与乐观并发,不适合做事件溯源的存储)。这两条若按旧资料照搬,是当下最典型的失败模式。

·关于"读得顺"的警告

先读这条 · 关于"读得顺"的警告

DDD 的内容尤其容易制造"我懂了"的错觉:名词面熟、例子直白、读起来毫不费力。但建模能力不是读出来的,是在含糊的业务描述前画错、被驳、重画练出来的。下面三句心里话一旦冒出来,恰恰说明你还没学进去——这是流畅性错觉:

· 「我读得很顺」——顺,往往是因为词面熟,不是因为真能复述机制。合上页面,能讲清"聚合该多大由什么决定"吗?

· 「我做题很快」——题答得快,往往是因为碰到的是背过的标准题型;把同一判定换个业务壳,就未必答得动。

· 「我没卡壳」——没卡壳,往往是因为你只在看字,没在脑子里拿「云市」真跑一遍边界与事务。每章末的自测、09 章的综合演练,就是用来戳破这层错觉的,别跳过。

·怎么读这份教程

九章按"战略 → 桥 → 战术 → 架构 → 自检"递进,全部用同一个「云市」示例,后面的章节会反复提取前面建立的概念。建议顺序读,02→03→04→05 是主干,别跳。

01
领域与语言
领域 = 问题,模型 = 一次有取舍的抽象,统一语言(Ubiquitous Language)是绑住口语与代码的绳子;语言的作用域就是限界上下文;先点名贫血模型这个反派。
02
战略设计
子域三分(核心 / 支撑 / 通用)、问题空间 vs 解空间、投资拨盘:核心域自研、通用域买、支撑域 CRUD——把钱花在产生差异的地方。
03
限界上下文
语义边界、一词多义(商品 / 客户在五个上下文里是不同的类)、上下文 ≠ 微服务、上下文映射的九种关系、与团队拓扑的关联。
04
事件风暴
从一面事件墙推出 命令 → 聚合 → 读模型 → 策略;三个缩放层级;承上启下——把 02、03 的边界落到 05 的聚合。
05
聚合
一致性边界、聚合根、真正不变量、"一个事务一个聚合"源自乐观锁、按 ID 引用、小聚合;含实体(Entity)与值对象(Value Object)。
06
战术构件
仓储(Repository)、工厂(Factory)、领域服务 ≠ 应用服务(钉死最易混淆的一对)、模块——领域服务装规则、应用服务只编排。
07
领域事件
领域事件、最终一致性、Vernon"谁的职责"判定规则、领域事件 ≠ 事件驱动架构、Saga / 幂等 / 补偿。
08
架构与陷阱
六边形 / 整洁 / 洋葱(依赖一律指向内)、CQRS、事件溯源、限界上下文 ≠ 微服务 1:1、何时不用 DDD、常见陷阱合集。
09
自检
辨析判断题 + 从一段业务描述跑通 事件风暴 → 上下文 → 聚合 → 构件 的综合演练 + 亲手画一张图 + "何时退回 CRUD"清单。

DDD 与同库其他主题互为前后置:先有设计模式的对象建模直觉,DDD 的战术构件才好理解;DDD 划出的边界与领域事件,又落在分布式系统与 Spring 的工程实现上。