title: 架构师学习-DDD author: Gamehu date: 2025-07-11 21:54:50 tags:
ByteByteGo 的 Alex Xu 在《Domain-Driven Design (DDD) Demystified》中指出:"DDD 是一种以业务领域——而非数据库 schema 或最新框架——为决策中心的软件设计方法。"它要求工程师在整个项目生命周期中与领域专家深度协作,而不仅仅是收集一次需求就消失到 Jira 工单里[^1]。
David Laribee 在 MSDN Magazine(2009年2月)中也给出了经典定义:"DDD 是一系列原则和模式的集合,帮助开发者构建优雅的对象系统。恰当应用它可以产生被称为领域模型的软件抽象,这些模型封装了复杂的业务逻辑,弥合了业务现实与代码之间的鸿沟。"[^2]
DDD 不会自动生成代码,也无法魔法般修复一个遗留单体。但它提供的价值更为深远:清晰——清晰知道系统应该做什么,以及它被允许在哪里变化[^1]。
根据 ByteByteGo 的分析,DDD 在以下场景最具价值[^1]:
一个值得注意的洞见是:DDD 不关心架构是单体还是微服务,它关心的是模型是否反映领域的真实规则和语言,以及该模型能否随领域的变化安全演进[^1]。
David Laribee 用柏拉图的洞穴寓言来类比 DDD 的理念[^2]:
柏拉图提出,我们通过感官直觉和感知到的概念、人、地点和事物,仅仅是真相的"影子"。他称真正的"事物"为理型(Form)。
在洞穴寓言中,被锁在洞穴中的人只能看到洞壁上的影子。当动物走过洞口时,影子投射到墙上——洞中人以为那就是真实的事物。但实际上,那只是真实"理型"的影子。
在 DDD 中,你要追求的完美领域模型就像柏拉图的"理型"。这个理想模型散落在领域专家的头脑中、利益相关者的期望里、行业规范的要求间——这些都可以被理解为洞中人所看到的"影子"。而编程语言的限制、时间和预算的约束,就是洞中人只能看到墙壁的局限。
Eric Evans 将这个过程称为 "将知识碾碎进模型"(Crunching Knowledge into Models)[^2]——当你对领域有了重要的新认识,你知道应该把它体现在代码的哪里。
构建一个在线电子商务系统(E-Shop)的设计示例。这个示例将从宏观的战略设计(如何划分系统边界)到微观的战术设计(代码层面的核心元素),全面展示 DDD 的核心思想。
DDD 的首要原则是软件必须植根于领域,并且模型需要有清晰的边界。
场景: 假设我们要构建一个大型在线商店,不仅涉及用户下单,还需要处理库存发货和销售报表。
传统问题: 许多团队会试图创建一个包含所有属性(如用户、订单、商品、库存、报表)的单一庞大模型。这会导致模型臃肿,不同职能的团队互相干扰。
DDD 解决方案: 我们将系统划分为两个独立的界定的上下文(Bounded Contexts),:
核心思想:
David Laribee 在 MSDN Magazine 中特别强调了防腐层(Anti-Corruption Layer, ACL)的价值:它是阻止非领域概念泄漏到模型中的"守门人"。有意思的是,Repository 模式本身也是一种 ACL——它将 SQL 或 ORM 构造隔离在领域模型之外[^2]。
关于限界上下文的实践,MSDN 文章特别提到了 Greg Young——他是在架构层面运用 Bounded Contexts(如分离事务库与报表库)的专家,生产了大量关于该主题的优质内容[^2]。
在“在线交易上下文”内部,我们使用战术模式来构建领域模型。
开发人员与业务专家(如销售经理)共同制定一套语言。
为了隔离关注点,我们将系统分为四层,:
Order、Customer 等业务对象和规则。这是软件的心脏。这是领域模型的基本构建块。
实体(Entity):
Order(订单)。Order 是一个实体,必须有唯一的标识符(Identity),。值对象(Value Object):
Address(送货地址)。Address 应该是不可变的(Immutable),如果客户搬家了,我们是用一个新的 Address 对象替换旧的,而不是修改旧对象,。为了保证数据一致性,我们需要划定修改数据的边界。
Order(订单)可能包含多个 OrderItem(订单项)。Order 是这个聚合的根(Aggregate Root)。Order),不能直接引用内部的 OrderItem。如果想修改某个订单项的数量,必须通过根的方法(如 order.updateItemQuantity())来进行。这确保了订单总价等不变量(Invariants)在修改过程中始终保持一致,。有些动作不属于特定的对象。
CheckoutService(结账服务)或 FundTransferService(转账服务)。Order 或 Customer 都不合适,因此我们创建一个无状态的领域服务来封装这些操作,。领域服务 vs 应用服务:David Laribee 区分了这两种服务[^2]:
- 领域服务(Domain Service):代表不以特定实体为中心的领域过程或操作,通常以动词或业务活动命名,是通用语言的一部分。例如
PolicyRenewalProcessor。- 应用服务(Application Service):用于引入分层架构,负责将模型数据映射为客户端所需的形式,或集成多个模型。例如将领域模型通过 WCF 暴露给外部系统。
为了解耦领域模型与数据库。
OrderRepository。findOrder(id),不关心底层是 SQL Server 还是 Oracle。资源库负责从数据库中检索数据并将其重建为领域对象(如 Order 聚合)。这让开发人员可以像从内存集合中获取对象一样获取领域对象,而无需在业务逻辑中编写 SQL,。David Laribee 明确指出:"持久化是基础设施"(Persistence is infrastructure)。Repository 的价值在于阻止 SQL 语句或存储过程等持久化概念混入领域模型。他同时指出,很多人会声称"数据库就是模型"——这在高度规范化并辅以图形化工具时确实能传达很多领域信息,但纯数据建模技术(如 ERD 或类图)无法展现一个领域中固有的行为——你需要看到模型的各部分如何运动、类型之间如何协作来完成工作[^2]。
结合上述概念,一个“用户修改收货地址”的业务场景在代码设计中如下流转:
CustomerRepository)。Customer 聚合(实体)。Customer 实体的业务方法(如 customer.moveTo(newAddress))。Customer 实体将旧的 Address 值对象替换为新的 Address 值对象。Customer 聚合保存回数据库。如果想深入学习 DDD,了解谁是真正的"意见领袖"非常重要。以下是技术界公认的 Top 级人物与他们的核心阵地:
毫无疑问,Eric Evans 是 DDD 的"亲生父亲"。他在 2003 年出版了奠基之作《领域驱动设计:软件核心复杂性应对之道》(*Domain-Driven Design: Tackling Complexity in the Heart of Software*),也就是技术圈常说的"蓝皮书"。
如果说 Eric Evans 定义了 DDD 的理论框架,那 Vaughn Vernon 就是将 DDD 真正工程化、可落地的关键人物。
IDDD_Samples)。Vlad Khononov 撰写了近几年广受好评的《学习领域驱动设计》(*Learning Domain-Driven Design*,O'Reilly,2021),因封面设计被称为"黑白皮书"。
在 MSDN Magazine(2009年)中,David Laribee 特别推荐跟踪 Greg Young 的博客,称其在架构层面运用 Bounded Contexts(如分离事务数据库与报表数据库)方面"经验丰富且表达清晰"[^2]。Greg Young 后来也以 CQRS(命令查询职责分离)模式的推广者而广为人知。
经典著作:
在线资源:
经典文章:
[^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