架构师学习-DDD

架构师学习 第2篇
## **写在前面**
DDD 我在上一家公司的时候就专门学习过,也做了一些实践,可能因为我当时没有太深入的了解,所以总觉得好像有些重,有一种概念大于实操的感觉,不过有些概念定义对我还是很有用的,刚好当下做架构回过头再好好学习下

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. 领域层(核心): 包含 OrderCustomer 等业务对象和规则。这是软件的心脏
  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(转账服务)。
  • 设计理由: 结账可能涉及订单状态更新、库存扣减、支付网关调用等。这些行为放入 OrderCustomer 都不合适,因此我们创建一个无状态的领域服务来封装这些操作,。

领域服务 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

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)—— 现代最佳入门
  • 在线资源:

  • 经典文章:

    • 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