zeno2333

zeno2333/DDD初探:what&why&how

Created Fri, 10 Jul 2026 17:20:00 +0800 Modified Fri, 10 Jul 2026 17:20:00 +0800

最近读了 Domain Driven Design Quickly,这本小册子不长,但很适合作为 DDD 的第一块垫脚石。它不是一本“教你怎么写框架代码”的书,也不是一本“照着抄几个模式就能上手”的书。它更像是在反复强调一件事:复杂软件真正难的地方,通常不在技术本身,而在于你有没有把业务领域理解清楚,并准确地映射进代码。

这篇文章算是一份入门读书笔记,试着用三个问题串起来:whatwhyhow

What:DDD 到底是什么

如果只用一句话概括,我会把 DDD 理解成:

用领域模型作为软件设计的核心,把业务概念、业务规则和代码结构尽量对齐。

书里反复强调,软件不是脱离现实世界独立存在的。大多数软件最终都服务于某个具体领域:银行、物流、电商、航班管制、医疗、风控等等。既然软件是为领域问题服务的,那么软件模型就不应该只是“表结构 + 接口 + CRUD”的堆叠,而应该尽量成为领域本身的抽象表达。

这也是 DDD 和“先写代码再补理解”的思路差别最大的地方。DDD 认为:

  • 软件首先要反映领域,而不只是反映数据库。
  • 模型首先要帮助人理解问题,然后才帮助程序运行。
  • 代码不只是实现细节,也应该是领域知识的载体。

所以 DDD 不是某个单点技巧,它更像是一套设计立场:让模型站在中间,一头连接业务,一头连接实现。

Why:为什么需要 DDD

1. 因为复杂度主要来自业务,而不是语法

简单系统当然可以直接写,但一旦进入复杂业务,问题很快就不是“这个功能怎么做”,而是:

  • 这个概念在业务里到底是什么意思?
  • 两个看起来相似的对象,本质上是不是同一个东西?
  • 某条规则应该放在谁身上?
  • 某次状态变更,边界到底在哪里?

这类问题如果不先在模型层想清楚,后面的实现就会不断返工。

2. 因为团队沟通成本会吞掉设计质量

书里我觉得最重要的一点,不是某个战术模式,而是 Ubiquitous Language,也就是统一语言。

现实项目里最常见的问题不是不会写代码,而是不同角色口中的同一个词根本不是一回事:

  • 产品说的“订单”,可能包含支付前和支付后两种状态。
  • 开发说的“用户”,可能其实是登录账号,不是业务上的客户。
  • 数据库里的某张表名,可能已经偏离真实业务概念很多年了。

一旦语言不统一,模型一定会漂。模型一漂,代码就会继续漂。最后所有人都在用各自的术语交流,但没有人真正说的是同一件事。

DDD 的价值之一,就是要求团队把“说出来的话”和“写下来的代码”对齐。

3. 因为代码结构会反过来影响业务演进

如果系统只是按页面、接口、表结构堆起来,那么一开始改得很快,但后面业务一变化,代码就会越来越脆。因为代码里根本没有表达“为什么这样设计”,只有“现在先这么跑起来”。

DDD 想解决的是这个问题:当业务变化时,代码能不能沿着已有模型自然演化,而不是每次都靠补丁修补。

How:DDD 应该怎么做

Domain Driven Design Quickly 给出的路径很清晰,我把它粗略拆成四层。

1. 先和领域专家一起建模

DDD 的起点不是画类图,而是理解领域。

书里举了空中交通监控的例子:一开始大家会提到飞机、起点、终点;继续往下谈,才慢慢抽出更关键的概念,比如 routefix、坐标点、偏航、冲突判断。这说明一个好的模型不是拍脑袋想出来的,而是在和领域专家反复对话中提炼出来的。

这一阶段最重要的不是“马上得出正确答案”,而是持续逼近更准确的抽象。

我的理解是:

  • 先找业务里真正稳定的重要概念。
  • 再判断这些概念之间的关系。
  • 最后再把这些关系翻译为代码结构。

如果顺序反了,很容易写成“实现先行,语义滞后”。

2. 用统一语言把模型固定下来

统一语言不是文档装饰,而是模型的外壳。

一个词如果在会议里这样叫,在代码里却换了另一个名字,那么模型就已经开始断裂了。DDD 的要求很严格:讨论时用的概念、文档里的术语、类名和方法名,最好都来自同一套语言。

这件事听上去很朴素,但价值非常大:

  • 它降低了业务和技术之间的翻译损耗。
  • 它让代码本身变成知识表达。
  • 它让重构不只是改结构,也是改理解。

很多时候,重命名一个对象,其实不是代码整理,而是认知升级。

3. 用模型驱动设计落到代码里

这本书在战术层面给了一组经典 building blocks,我把它们当成“模型落地时最常用的工具箱”。

Entity

当一个对象最重要的是“身份连续性”而不是属性值时,它更适合建成实体。

最典型的判断方式不是“字段多不多”,而是:两个属性完全一样的对象,有没有可能仍然是两个不同的业务对象?

如果答案是有,那大概率就是 Entity。

Value Object

如果一个对象不靠身份区分,而是靠它承载的值来定义自己,那它更适合做值对象。

书里很强调值对象最好保持不可变。我很认同这一点,因为不可变值对象天然更容易共享、推理和测试。

Service

有些行为很重要,但它不天然属于某个实体或值对象。这时硬塞进去只会让对象变脏,比较自然的做法是抽成领域服务。

我对这部分最大的提醒是:Service 不是行为垃圾桶。 只有当某个行为确实横跨多个对象、又不适合挂在单个对象上时,才值得抽出去。

Module

模块的意义不是分文件夹,而是组织概念。好的模块划分,应该能帮助人理解系统,而不是只帮助 IDE 展开目录树。

Aggregate

这是我读下来最值得反复消化的一块。

我以前对聚合的直觉经常停留在“对象组合”上,但书里更强调它的本质是:

  • 一组需要一起维护一致性的对象边界
  • 对外只暴露聚合根
  • 所有修改都应通过聚合根受控发生

换句话说,聚合首先是一致性边界,其次才是对象结构。

Factory / Repository

这两个模式更像是生命周期管理工具:

  • Factory 解决“复杂对象怎么被正确创建”
  • Repository 解决“领域对象怎么被获取和恢复”

它们的核心价值不是帮你少写几行代码,而是避免创建逻辑和存取逻辑污染领域模型。

4. 在大系统里承认边界,而不是假装只有一个模型

这本书后半部分最关键的战略概念是 Bounded Context

这部分我读完最有感触,因为很多系统并不是没有模型,而是把不该混在一起的模型硬混在一起了。最后结果就是:

  • 同一个词在不同子系统里含义不同
  • 团队之间对“正确模型”没有共识
  • 一个系统里的改动会不断污染另一个系统

DDD 的做法不是强行统一一切,而是先划边界:

  • 在这个上下文里,某个概念是什么意思?
  • 超出这个边界后,它是不是还是同一个意思?
  • 团队、代码库、数据库 schema 的边界是否和模型边界一致?

进一步,Context Map 让我们显式定义上下文之间的关系。书里列出的几种关系都很经典:

  • Shared Kernel
  • Customer-Supplier
  • Conformist
  • Anticorruption Layer
  • Open Host Service
  • Separate Ways

如果只让我选一个最实用的,我会选 Anticorruption Layer。因为真实世界里,几乎每个稍微大一点的系统都要和“不是你设计的东西”打交道:遗留系统、第三方系统、历史接口、奇怪的数据源。与其让外部模型直接渗进来,不如明确放一道翻译层,把污染挡在边界之外。

我现在对 DDD 的几个阶段性理解

读完这本小册子后,我对 DDD 的理解大概收敛成下面几点:

1. DDD 首先解决的是理解问题,不是代码模式问题

如果团队还没把领域概念讲清楚,就急着讨论 Repository 怎么写、Aggregate 怎么拆,通常都太早了。

2. 模型不是画出来就结束,而是要随着理解持续演化

书里很强调 Refactoring Toward Deeper Insight。这点我很喜欢。好的模型往往不是一开始就设计对,而是在持续命名、持续讨论、持续重构中变得更深。

3. 聚合不是“尽可能大”,而是“一致性控制刚刚好”

如果聚合边界画得过大,修改成本和并发冲突都会上升;如果过小,又守不住业务不变量。它不是对象树问题,而是事务边界问题。

4. 边界上下文几乎是大型系统的必修课

系统一旦复杂到多个团队、多套模型、多条业务线同时演进,Bounded Context 就不是“高级概念”,而是避免语义内耗的基础设施。

5. DDD 并不适合所有系统

如果问题本身并不复杂,业务规则也很薄,系统更接近简单的信息录入和展示,那么上来就全套 DDD 可能会过度设计。DDD 最有价值的地方,还是在复杂领域、强规则、高演化压力的场景。

结语

Domain Driven Design Quickly 这本书最好的地方,在于它没有把 DDD 讲成一套玄学,也没有把它简化成几个标签。它给我的最大提醒是:

软件设计不是先决定技术栈,再把业务塞进去;而是先形成对领域的有效抽象,再让实现去服从这个抽象。

如果说这次阅读给我留下了一个最短总结,那就是:

  • what:DDD 是以领域模型为中心的软件设计方法
  • why:因为复杂度主要来自领域,而语言和模型失真会迅速拖垮系统
  • how:通过统一语言、模型驱动设计、战术建模模式和边界上下文,把理解一步步落实到代码里

这篇还只是初探。后面如果继续读 Evans 的原书,或者结合实际项目再看聚合、仓储和上下文映射,应该会有更具体的体会。