title: 架构师学习-DDD
author: Gamehu
date: 2025-07-11 21:54:50
tags:
- DDD
categories:
- 架构师
---
架构师学习
第2篇
## **写在前面**
DDD 我在上一家公司的时候就专门学习过,也做了一些实践,可能因为我当时没有太深入的了解,所以总觉得好像有些重,有一种概念大于实操的感觉,不过有些概念定义对我还是很有用的,刚好当下做架构回过头再好好学习下
{%asset_img 1.png %}
{%asset_img ddd-01.png %}
## DDD的核心理念:为什么需要DDD?
大多数软件出问题,不是因为语法错误或 if-else 逻辑有 bug,而是因为团队与业务问题失去了对齐。
ByteByteGo 的 Alex Xu 在《Domain-Driven Design (DDD) Demystified》中指出:"DDD 是一种以业务领域——而非数据库 schema 或最新框架——为决策中心的软件设计方法。"它要求工程师在整个项目生命周期中与领域专家深度协作,而不仅仅是收集一次需求就消失到 Jira 工单里[^1]。
David Laribee 在 MSDN Magazine(2009年2月)中也给出了经典定义:"DDD 是一系列原则和模式的集合,帮助开发者构建优雅的对象系统。恰当应用它可以产生被称为领域模型的软件抽象,这些模型封装了复杂的业务逻辑,弥合了业务现实与代码之间的鸿沟。"[^2]
### DDD不是什么"银弹"
DDD 不会自动生成代码,也无法魔法般修复一个遗留单体。但它提供的价值更为深远:**清晰**——清晰知道系统应该做什么,以及它被允许在哪里变化[^1]。
### DDD最适合什么场景?
根据 ByteByteGo 的分析,DDD 在以下场景最具价值[^1]:
1. **领域非平凡且在持续演进**——金融、医疗、物流、大型电商等
2. **多个团队在系统的重叠部分工作**——需要统一的语言和清晰的边界
3. **代码需要反映真实业务行为**——而不是抽象的技术构造
一个值得注意的洞见是:**DDD 不关心架构是单体还是微服务,它关心的是模型是否反映领域的真实规则和语言,以及该模型能否随领域的变化安全演进**[^1]。
### 柏拉图式的模型:代码是领域的"影子"
David Laribee 用柏拉图的洞穴寓言来类比 DDD 的理念[^2]:
> 柏拉图提出,我们通过感官直觉和感知到的概念、人、地点和事物,仅仅是真相的"影子"。他称真正的"事物"为**理型(Form)**。
>
> 在洞穴寓言中,被锁在洞穴中的人只能看到洞壁上的影子。当动物走过洞口时,影子投射到墙上——洞中人以为那就是真实的事物。但实际上,那只是真实"理型"的影子。
在 DDD 中,你要追求的完美领域模型就像柏拉图的"理型"。这个理想模型散落在领域专家的头脑中、利益相关者的期望里、行业规范的要求间——这些都可以被理解为洞中人所看到的"影子"。而编程语言的限制、时间和预算的约束,就是洞中人只能看到墙壁的局限。
Eric Evans 将这个过程称为 **"将知识碾碎进模型"(Crunching Knowledge into Models)**[^2]——当你对领域有了重要的新认识,你知道应该把它体现在代码的哪里。
## 示例
构建一个**在线电子商务系统(E-Shop)**的设计示例。这个示例将从宏观的战略设计(如何划分系统边界)到微观的战术设计(代码层面的核心元素),全面展示 DDD 的核心思想。
### 1. 战略设计:界定的上下文 (Bounded Contexts)
DDD 的首要原则是软件必须植根于领域,并且模型需要有清晰的边界。
**场景:** 假设我们要构建一个大型在线商店,不仅涉及用户下单,还需要处理库存发货和销售报表。
**传统问题:** 许多团队会试图创建一个包含所有属性(如用户、订单、商品、库存、报表)的单一庞大模型。这会导致模型臃肿,不同职能的团队互相干扰。
**DDD 解决方案:**
我们将系统划分为两个独立的**界定的上下文(Bounded Contexts)**,:
1. **在线交易上下文(E-Shop Context):** 关注客户下单、购物车、结账。这里的“商品”关注价格和描述。
2. **报表上下文(Reporting Context):** 关注销售趋势、库存周转。这里的“商品”可能只关注销售数量和成本,不需要描述信息。
**核心思想:**
* **消除歧义:** 同一个词(如“商品”)在不同上下文中可能有不同的含义和属性。通过划分上下文,我们可以保证模型在各自边界内的纯洁性和一致性。
* **上下文映射(Context Map):** 我们定义这两个上下文的关系。例如,报表系统需要从交易系统获取数据,它们可能通过**客户-供应商(Customer-Supplier)**模式交互,或者通过**防崩溃层(Anticorruption Layer)**来转换数据,确保报表系统的模型不受交易系统模型变更的直接破坏,。
> David Laribee 在 MSDN Magazine 中特别强调了**防腐层(Anti-Corruption Layer, ACL)**的价值:它是阻止非领域概念泄漏到模型中的"守门人"。有意思的是,Repository 模式本身也是一种 ACL——它将 SQL 或 ORM 构造隔离在领域模型之外[^2]。
> 关于限界上下文的实践,MSDN 文章特别提到了 **Greg Young**——他是在架构层面运用 Bounded Contexts(如分离事务库与报表库)的专家,生产了大量关于该主题的优质内容[^2]。
### 2. 战术设计:领域模型的核心要素
在“在线交易上下文”内部,我们使用战术模式来构建领域模型。
#### A. 通用语言 (Ubiquitous Language)
开发人员与业务专家(如销售经理)共同制定一套语言。
* **示例:** 大家不再说“插入一条记录到订单表”,而是统一说“**提交订单(Submit Order)**”。
* **价值:** 这消除了沟通障碍,代码中的类名和方法名将直接反映业务意图,。
#### B. 分层架构 (Layered Architecture)
为了隔离关注点,我们将系统分为四层,:
1. **用户界面层:** 展示商品页面,接收用户点击。
2. **应用层:** 协调任务(如“协调结账流程”),但不包含业务逻辑。
3. **领域层(核心):** 包含 `Order`、`Customer` 等业务对象和规则。**这是软件的心脏**。
4. **基础设施层:** 处理数据库持久化、发送邮件等技术实现。
#### C. 实体 (Entities) 与 值对象 (Value Objects)
这是领域模型的基本构建块。
* **实体(Entity):**
* **示例:** `Order`(订单)。
* **设计理由:** 订单有生命周期(从创建到支付到发货),并且需要被追踪。即使两个订单的内容完全一样,只要 ID 不同,它们就是不同的对象。因此,`Order` 是一个实体,必须有唯一的标识符(Identity),。
* **值对象(Value Object):**
* **示例:** `Address`(送货地址)。
* **设计理由:** 我们只关心地址的属性(街道、城市),而不关心它的唯一标识。如果两个客户住在同一地址,这在业务上是等价的。`Address` 应该是不可变的(Immutable),如果客户搬家了,我们是用一个新的 `Address` 对象替换旧的,而不是修改旧对象,。
#### D. 聚合 (Aggregates)
为了保证数据一致性,我们需要划定修改数据的边界。
* **示例:** 一个 `Order`(订单)可能包含多个 `OrderItem`(订单项)。
* **设计:** `Order` 是这个**聚合的根(Aggregate Root)**。
* **规则:** 外部对象只能引用根(`Order`),不能直接引用内部的 `OrderItem`。如果想修改某个订单项的数量,必须通过根的方法(如 `order.updateItemQuantity()`)来进行。这确保了订单总价等不变量(Invariants)在修改过程中始终保持一致,。
#### E. 服务 (Services)
有些动作不属于特定的对象。
* **示例:** `CheckoutService`(结账服务)或 `FundTransferService`(转账服务)。
* **设计理由:** 结账可能涉及订单状态更新、库存扣减、支付网关调用等。这些行为放入 `Order` 或 `Customer` 都不合适,因此我们创建一个无状态的**领域服务**来封装这些操作,。
> **领域服务 vs 应用服务**:David Laribee 区分了这两种服务[^2]:
> - **领域服务(Domain Service)**:代表不以特定实体为中心的领域过程或操作,通常以动词或业务活动命名,是通用语言的一部分。例如 `PolicyRenewalProcessor`。
> - **应用服务(Application Service)**:用于引入分层架构,负责将模型数据映射为客户端所需的形式,或集成多个模型。例如将领域模型通过 WCF 暴露给外部系统。
#### F. 资源库 (Repositories)
为了解耦领域模型与数据库。
* **示例:** `OrderRepository`。
* **设计:** 领域层只定义接口 `findOrder(id)`,不关心底层是 SQL Server 还是 Oracle。资源库负责从数据库中检索数据并将其重建为领域对象(如 `Order` 聚合)。这让开发人员可以像从内存集合中获取对象一样获取领域对象,而无需在业务逻辑中编写 SQL,。
> David Laribee 明确指出:**"持久化是基础设施"(Persistence is infrastructure)**。Repository 的价值在于阻止 SQL 语句或存储过程等持久化概念混入领域模型。他同时指出,很多人会声称"数据库就是模型"——这在高度规范化并辅以图形化工具时确实能传达很多领域信息,但纯数据建模技术(如 ERD 或类图)无法展现一个领域中固有的**行为**——你需要看到模型的各部分如何运动、类型之间如何协作来完成工作[^2]。
### 3. 系统运作流程示例
结合上述概念,一个“用户修改收货地址”的业务场景在代码设计中如下流转:
1. **应用层**接收请求,调用**资源库**(`CustomerRepository`)。
2. **资源库**利用基础设施层从数据库检索数据,重建 **`Customer` 聚合**(实体)。
3. **应用层**调用 `Customer` 实体的业务方法(如 `customer.moveTo(newAddress)`)。
4. 在方法内部,`Customer` 实体将旧的 `Address` **值对象**替换为新的 `Address` **值对象**。
5. **资源库**将更新后的 `Customer` 聚合保存回数据库。
---
### DDD思想领袖与经典著作
如果想深入学习 DDD,了解谁是真正的"意见领袖"非常重要。以下是技术界公认的 Top 级人物与他们的核心阵地:
#### 1. Eric Evans —— DDD 的开创者("蓝皮书"作者)
毫无疑问,Eric Evans 是 DDD 的"亲生父亲"。他在 2003 年出版了奠基之作《**领域驱动设计:软件核心复杂性应对之道**》(*Domain-Driven Design: Tackling Complexity in the Heart of Software*),也就是技术圈常说的**"蓝皮书"**。
* **核心贡献:** 提出了**限界上下文(Bounded Context)、通用语言(Ubiquitous Language)、聚合(Aggregate)、实体(Entity)、值对象(Value Object)**等 DDD 核心概念。David Laribee 称这本书为"业界最具经验的软件设计师之一所著的宝库"[^2]。
* **所在机构:** 创立了 **Domain Language** 咨询公司,至今依然在世界各地的顶级技术会议上发表 DDD 相关演讲。
* **经典思想:** "将知识碾碎进模型"(Crunching Knowledge into Models)——代码是领域理解的不断演进,而非一次性的需求文档翻译[^2]。
#### 2. Vaughn Vernon —— DDD 落地实践的第一人("红皮书"作者)
如果说 Eric Evans 定义了 DDD 的**理论框架**,那 Vaughn Vernon 就是将 DDD 真正**工程化、可落地**的关键人物。
* **核心著作:** 《**实现领域驱动设计**》(*Implementing Domain-Driven Design*,2013),被称为**"红皮书"**。这本书极大地补充了蓝皮书中缺失的实操细节和代码示例。后续又出版了《Reactive Messaging Patterns with the Actor Model》和《Strategic Monoliths and Microservices》。
* **开源贡献:** 在 GitHub 上提供了大量基于 Java / C# 等语言的 DDD 落地脚手架和示例代码(如 `IDDD_Samples`)。
* **个人网站:** [vaughnvernon.co](https://vaughnvernon.co)
#### 3. Vlad Khononov —— 现代 DDD 的集大成者("黑白皮书"作者)
Vlad Khononov 撰写了近几年广受好评的《**学习领域驱动设计**》(*Learning Domain-Driven Design*,O'Reilly,2021),因封面设计被称为**"黑白皮书"**。
* **核心贡献:** 他成功地将经典 DDD 理论与**微服务、云原生架构、现代企业架构**进行了完美桥接,是目前最符合当下技术栈的 DDD 权威读物。如果你觉得蓝皮书太厚太理论,黑白皮书是更好的入门选择。
---
#### 特殊贡献者
##### Alberto Brandolini —— Event Storming(事件风暴)发明人
* **核心贡献:** 提出了**事件风暴(Event Storming)**这一工作坊形式。这是目前全球公认在业务梳理、限界上下文发现、微服务边界识别中最有效的协作建模方法之一。
* **代表著作:** 《Introducing EventStorming: An act of Deliberate Collective Learning》
* **核心理念:** DDD 不只是编码,更是**团队协作和知识发现**的过程。
##### Greg Young —— CQRS 与架构级限界上下文的先驱
在 MSDN Magazine(2009年)中,David Laribee 特别推荐跟踪 Greg Young 的博客,称其在架构层面运用 Bounded Contexts(如分离事务数据库与报表数据库)方面"经验丰富且表达清晰"[^2]。Greg Young 后来也以 **CQRS(命令查询职责分离)**模式的推广者而广为人知。
---
### 学习资料
* **经典著作:**
* Eric Evans - *Domain-Driven Design*(蓝皮书,2003)—— DDD 圣经
* Vaughn Vernon - *Implementing Domain-Driven Design*(红皮书,2013)—— 落地指南
* Vlad Khononov - *Learning Domain-Driven Design*(黑白皮书,2021)—— 现代最佳入门
* **在线资源:**
* [domain-driven-hexagon](https://github.com/Sairyss/domain-driven-hexagon) —— DDD 与六边形架构的 TypeScript 实战脚手架
* [IDDD_Samples](https://github.com/VaughnVernon/IDDD_Samples) —— Vaughn Vernon 的官方代码示例
* [Yahoo! DDD Group](https://tech.groups.yahoo.com/group/domaindrivendesign) —— Eric Evans 书中推荐的早期 DDD 社区[^2]
* **经典文章:**
* David Laribee, *Best Practice - An Introduction To Domain-Driven Design*, MSDN Magazine, February 2009[^2]
* Alex Xu (ByteByteGo), *Domain-Driven Design (DDD) Demystified*, April 2025[^1]
---
[^1]: Alex Xu, "Domain-Driven Design (DDD) Demystified," ByteByteGo Newsletter, April 24, 2025. https://blog.bytebytego.com/p/domain-driven-design-ddd-demystified
[^2]: David Laribee, "Best Practice - An Introduction To Domain-Driven Design," MSDN Magazine, Vol. 24 No. 02, February 2009. https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/february/best-practice-an-introduction-to-domain-driven-design