一句话理解DDD:用业务语言统一代码和需求,让复杂系统按业务边界清晰拆分。

领域驱动设计(DDD)近年来备受关注,尤其在企业软件和SaaS领域。但很多开发者读完《领域驱动设计》后依然感到困惑——概念太抽象,落地太难。

本文将通过一个虚拟公司的实际案例,带你从零开始理解DDD,掌握战略设计与战术设计的核心要点,最终实现DDD在项目中的落地。

一、为什么需要领域模型?

传统的MVC模式在简单业务场景下运作良好,但随着业务复杂度增加,问题逐渐暴露:

  1. 缺乏业务语言:MVC仅反映软件层面架构,无法直接与业务对话

  2. 数据与行为分离:数据库存储数据,服务层实现行为,容易导致需求理解偏差

  3. 边界模糊:缺乏明确的边界划分规范,大规模团队协作容易职责不清

领域模型的价值在于:

  • 统一语言:让产品、设计、开发用同一套术语沟通

  • 业务与技术解耦:领域模型和数据模型分离,聚焦业务本质

  • 知识沉淀:模型与软件实现关联,业务知识得以传递和积累

二、DDD核心概念速览 2.1 战略设计:从全局视角规划

战略设计解决"怎么拆"的问题,主要包括:

概念

示例

领域

组织要做的全部事情

知识付费领域

子域

领域中独立的业务板块

订阅域、专栏域、金融域

核心域

决定核心竞争力的子域

订阅域(付费订阅是核心业务)

通用域

被多个子域使用的通用功能

权限域、登录域

支撑域

支撑其他业务的企业特性功能

评论域、专栏域

限界上下文

业务边界的划分,对应微服务

专栏订阅上下文

2.2 战术设计:具体实现层面

战术设计解决"怎么实现"的问题,主要包括:

  • 实体:有唯一标识和生命周期的业务对象(如:订阅、专栏)

  • 值对象:无标识、不可变的属性组合(如:地址、金额)

  • 聚合:一组有相同生命周期的实体和值对象的集合

  • 聚合根:聚合对外的唯一入口

  • 领域服务:不属于任何实体的领域行为

  • 资源库:封装数据访问逻辑

  • 领域事件:领域内发生的重要事情

三、实战案例:RabbitTech知识付费平台

假设我们是"RabbitTech"公司的CTO,需要设计一个知识付费产品"RabbitAdvisors"。

3.1 业务场景分析

核心业务流程:

  1. 作者开设专栏

  2. 用户浏览专栏

  3. 用户付费订阅

  4. 用户阅读内容

  5. 公司与作者分成

3.2 领域划分

知识付费领域
├── 订阅域(核心域)- 订阅、订单
├── 专栏域(支撑域)- 专栏、文章
├── 金融域(支撑域)- 分成、结算
├── 用户域(通用域)- 用户、权限
└── 评论域(支撑域)- 评论、点赞
3.3 限界上下文划分

┌─────────────────────────────────────┐
│ 专栏订阅上下文(微服务1) │
│ ┌─────────┐ ┌─────────┐ │
│ │ 订阅域 │ ←─── │ 订单域 │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────┘


┌─────────────────────────────────────┐
│ 专栏管理上下文(微服务2) │
│ ┌─────────┐ ┌─────────┐ │
│ │ 专栏域 │ ←─── │ 文章域 │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────┘
3.4 聚合设计示例

以"订阅聚合"为例:

订阅聚合

├── 订阅(聚合根)
│ ├── id: SubscriptionId
│ ├── userId: UserId
│ ├── columnId: ColumnId
│ ├── status: SubscriptionStatus
│ └── subscribedAt: DateTime

├── 订阅项(实体)
│ └── 文章阅读记录

└── 值对象
├── 订阅时长
└── 付款金额
四、统一语言:团队协作的基石

统一语言是DDD的核心实践,它要求团队在所有沟通中使用一致的术语。

4.1 定义统一语言

以"用户订阅专栏"为例:

术语

定义

用户(User)

在RabbitAdvisors注册过的人

专栏(Column)

作者创建的内容集合

订阅(Subscription)

用户付费购买专栏的记录

订单(Order)

用户支付行为的记录

4.2 在代码中应用统一语言

// 统一语言体现在类名、方法名、变量名中
public class Subscription {
private SubscriptionId id;
private UserId userId;
private ColumnId columnId;
private SubscriptionStatus status;
public void activate() {
if (this.status != SubscriptionStatus.PENDING) {
throw new IllegalStateException("只有待激活的订阅可以激活");
}
this.status = SubscriptionStatus.ACTIVE;
}
}
五、DDD落地的关键实践 5.1 实体模型选择

实体有四种常见形态:

模型类型

特点

适用场景

失血模型

仅有数据定义和getter/setter

简单CRUD

贫血模型

包含部分业务逻辑

DDD推荐

充血模型

包含所有业务逻辑

复杂业务核心

胀血模型

包含非业务逻辑

不推荐

推荐实践:采用贫血模型,实体和领域服务共同构成领域模型。

5.2 聚合设计原则

  1. 一致性边界:聚合内的对象必须保持一致性

  2. 通过聚合根访问:外部只能通过聚合根访问聚合内对象

  3. 小聚合优先:聚合越小越好,降低并发冲突

  4. 最终一致性:聚合之间通过领域事件保持最终一致

5.3 限界上下文与微服务

限界上下文是微服务拆分的天然边界:

一个限界上下文 ≈ 一个微服务


划分原则:
- 支持完整的业务流程
- 团队自主性
- 独立部署能力
六、DDD不是银弹

DDD也有其适用边界:

适合DDD的场景

  • 业务复杂、规则多变

  • 团队规模较大

  • 领域专家参与度高

  • 期演进的产品

不适合DDD的场景

  • 简单CRUD系统

  • 快速原型验证

  • 小团队、短期项目

  • 技术驱动而非业务驱动

七、总结

DDD的本质不是技术,而是用业务视角思考和设计

  1. 战略设计告诉你系统该怎么拆

  2. 战术设计告诉你代码该怎么写

  3. 统一语言让团队说同一种话

  4. 领域模型是业务知识的沉淀

记住:DDD是方法论,不是框架。它提供的是一种思考方式,帮助你更好地理解和建模复杂业务领域。

本文代码示例可在以下仓库找到: 后端微服务:github.com/eyebluecn/smart-classroom-misc 前端项目:github.com/eyebluecn/smart-classroom-front 在线演示:classroom.eyeblue.cn