领域驱动设计 (DDD) 学习笔记

什么是 DDD?

领域驱动设计 (Domain-Driven Design, DDD) 是一种软件开发方法,通过将软件系统看作业务流程的反映,而非仅仅交付物。它更关注业务流程、业务术语和业务实践,技术关注点排在第二位。

核心思想:以纯净的领域模型来反映业务需求,代码即为需求文档。


培训资料

注:PDF 浏览览需要 HTML5 浏览器支持


为什么需要 DDD?

传统开发方式的问题

开发方式 特点 问题
瀑布式 预先设计,按阶段交付 领域知识单向流动,缺乏反馈
用例驱动 用例图描述需求 设计像事务脚本,功能重复
敏捷方法 小任务迭代,快速反馈 缺乏设计原则,可能过度设计

DDD 的区别

DDD 与其他方法论的本质区别:

传统方法:技术关注点 > 业务关注点
DDD 方法:业务关注点 > 技术关注点

DDD 将软件系统视为业务流程的反映

分层架构

DDD 将系统分为四层,职责清晰分离:

┌─────────────────────────────────────┐
│     用户界面层 (User Interface)      │
│     负责与用户交互,展示信息          │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│       应用层 (Application)           │
│       组织任务,协调各方              │
│   不包含业务规则,只作为协调作用      │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│       领域层 (Domain) ★             │
│   表达业务概念及规则,软件的核心      │
│   不包含任何与业务无关的逻辑          │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│     基础设施层 (Infrastructure)      │
│   为上层提供技术能力(持久化、AOP等) │
└─────────────────────────────────────┘

核心概念:通用语言 (Ubiquitous Language)

为什么需要通用语言?

领域专家和技术团队的工作性质不同,沟通时各自倾向于自己擅长的语言方式,削弱了交流效果。

通用语言的定义

通用语言是开发人员与领域专家之间的交流工具:

角色 使用方式
开发人员 描述系统中的工作、任务及功能
领域专家 讨论需求、开发计划等

关键原则:认识到对通用语言的更改就是对模型的更改。

UML vs 领域模型

对比项 UML 领域模型
用途 沟通解释手段 持久化的代码
完整性 不可能完全描述所有需求 代码即需求文档
同步成本 巨大 自动同步
定位 抛弃型产物 核心资产

UML 属于分析过程中的产物,相对适合表述核心业务,但不应追求完全描述。


什么是模型?

模型的特征

特征 说明
🧩 多部分组成 由不同部分组合而成
🎯 特定目的 用于特定目的
📐 抽象系统 对现实的抽象
🧠 认知工具 帮助理解和思考
📝 多种表示 语言、代码、图解等
🔢 多个共存 一个系统包含若干模型

新旧模型的区别

旧模型 DDD 新模型
关注基础架构和技术概念 完全集中在核心领域
关注技术实现 关注领域概念和问题

战术设计:领域模型元素

1. 实体 (Entity)

特征:具有全局唯一标识

传统数据对象 → 贫血对象 (POCO) → 充血对象 (DDD Entity)

2. 值对象 (Value Object)

定义:没有唯一标识,通过属性值判断相等性

举例:地址

场景 类型 原因
两个室友订购 Vancl 货物 Value Object 是否在同一地点不重要
两人申请宽带服务 Entity 运营商需要区分同一地点

3. 聚合 (Aggregate)

定义:一组相关对象的集合,作为数据修改的单元

聚合根 (Aggregate Root) 的规则:

规则 说明
🎯 全局标识 根具有全局唯一标识
🔒 本地标识 根内部实体具有本地标识(根内唯一)
🚫 外部隔离 根的外部不得引用其内部对象
📤 临时引用 只能通过根传递,临时使用或值对象副本
🔍 查询限制 只有根可直接通过数据库查询
🔗 根间引用 根内部可保持对其他根的引用
🗑️ 级联删除 父子对象关系,删除根时级联删除
约束完整性 提交修改时,整个根的所有规则必须通过

4. 领域服务 (Domain Service)

何时使用:当重要的进程或转换操作不属于任何实体或值对象时

// 领域服务示例
public class TransferService
{
    public void TransferMoney(Account from, Account to, Money amount)
    {
        // 这个操作不属于单个 Account
        // 涉及多个实体协作
    }
}

设计原则

✅ 应该 ❌ 避免
具有领域意义 把所有功能都放在服务中
无状态 让实体变成贫血对象
跨实体协作 领域职责与领域主体分离

警告:服务是容易被滥用的概念。贫血模型把所有功能放在服务中,导致代码充斥着方法拷贝。

5. 工厂 (Factory) 和仓储 (Repository)

模式 职责 逻辑类型
工厂 创建复杂对象 全是领域逻辑
仓储 持久化和查询 全是数据访问逻辑

协作方式

工厂创建对象 → 仓储持久化对象
仓储读取数据 → 工厂重建领域对象

柔性设计 (Supple Design)

设计目标

问题:复杂软件缺乏良好设计时,重构和组合变得困难

目的:使设计让人们乐于使用,而且易于作出修改

定义:柔性设计是一些具体模式,能够揭示深层次的底层模型,指导突破复杂性限制。

1. 释意接口 (Intention-Revealing Interfaces)

原则:命名类和操作时要描述效果和目的,而不是实现方式

❌ 不好的命名 ✅ 好的命名
process() calculateTotalPrice()
doIt() validateOrder()
handle() sendEmailNotification()

好处

  • 客户开发人员不必理解内部细节
  • 与通用语言保持一致
  • TDD 可促使站在客户角度思考

2. 无副作用的函数 (Side-Effect-Free Function)

问题:嵌套调用难以预测结果,产生意外副作用

解决方案

操作类型 特点 示例
函数 只返回结果,无副作用 calculateTax(amount)
命令 引起状态改变,不返回信息 saveOrder(order)

原则:尽可能把逻辑放到函数中,把命令隔离到简单操作中

3. 断言 (Assertion)

定义:明确表述操作的后置条件和类/Aggregate 的规则

契约式设计

断言类型 说明
后置条件 描述操作的副作用和结果
前置条件 表明需要满足的条件
固定规则 规定结束时对象的状态

实现方式

  • 编程语言直接编写断言
  • 自动化单元测试
  • 文档或图

4. 概念轮廓 (Conceptual Contour)

原则:把设计元素分解为内聚的单元,与领域中的重要划分相匹配

两种极端 问题
所有元素在一个大结构 功能重复,意义难懂
过度分解 更复杂,概念可能丢失

目标:得到一组可逻辑组合的简单接口

5. 孤立的类 (Standalone Class)

定义:低耦合的极致,类完全孤立,可单独研究和理解

好处

  • 减轻理解模块的负担
  • 低耦合是减少概念过载的基本办法

实现:把复杂计算提取到独立的值对象中

6. 闭合操作 (Closure Of Operation)

定义:操作的返回类型与参数类型相同

示例

// 闭合操作
public Money Add(Money other) { ... }
public Money Multiply(decimal factor) { ... }

// 非闭合操作
public string ToString() { ... }

好处:提供高层接口,不引入其他概念依赖


领域特定语言 (DSL)

什么是 DSL?

语言是一套共同采用的沟通符号、表达方式与处理规则。

DSL (Domain Specific Language) 是针对特定领域的语言。

DDD + DSL

通过 DSL 的方式来调整业务:

  • 将通用语言转化为可执行的 DSL
  • 代码即为业务规则
  • 业务专家可以直接”阅读”代码

总结

DDD 的核心价值

价值 说明
🎯 业务优先 技术服务于业务
🗣️ 统一语言 开发与业务同频
📐 清晰分层 职责明确分离
🔧 柔性设计 易于维护和扩展

DDD vs 传统方法

传统:业务需求 → 技术设计 → 代码实现 → 需求丢失
DDD:  业务需求 → 领域模型 → 代码实现 = 需求文档

推荐阅读

  • 《领域驱动设计》- Eric Evans
  • 《实现领域驱动设计》- Vaughn Vernon
  • 《领域驱动设计精粹》- Scott Millett

持续学习,持续实践! 📚