一句话理解DDD:用业务语言统一代码和需求,让复杂系统按业务边界清晰拆分。
领域驱动设计(DDD)近年来备受关注,尤其在企业软件和SaaS领域。但很多开发者读完《领域驱动设计》后依然感到困惑——概念太抽象,落地太难。
本文将通过一个虚拟公司的实际案例,带你从零开始理解DDD,掌握战略设计与战术设计的核心要点,最终实现DDD在项目中的落地。
一、为什么需要领域模型?
传统的MVC模式在简单业务场景下运作良好,但随着业务复杂度增加,问题逐渐暴露:
缺乏业务语言:MVC仅反映软件层面架构,无法直接与业务对话
数据与行为分离:数据库存储数据,服务层实现行为,容易导致需求理解偏差
边界模糊:缺乏明确的边界划分规范,大规模团队协作容易职责不清
领域模型的价值在于:
统一语言:让产品、设计、开发用同一套术语沟通
业务与技术解耦:领域模型和数据模型分离,聚焦业务本质
知识沉淀:模型与软件实现关联,业务知识得以传递和积累
战略设计解决"怎么拆"的问题,主要包括:
概念
示例
领域
组织要做的全部事情
知识付费领域
子域
领域中独立的业务板块
订阅域、专栏域、金融域
核心域
决定核心竞争力的子域
订阅域(付费订阅是核心业务)
通用域
被多个子域使用的通用功能
权限域、登录域
支撑域
支撑其他业务的企业特性功能
评论域、专栏域
限界上下文
业务边界的划分,对应微服务
专栏订阅上下文
2.2 战术设计:具体实现层面
战术设计解决"怎么实现"的问题,主要包括:
实体:有唯一标识和生命周期的业务对象(如:订阅、专栏)
值对象:无标识、不可变的属性组合(如:地址、金额)
聚合:一组有相同生命周期的实体和值对象的集合
聚合根:聚合对外的唯一入口
领域服务:不属于任何实体的领域行为
资源库:封装数据访问逻辑
领域事件:领域内发生的重要事情
假设我们是"RabbitTech"公司的CTO,需要设计一个知识付费产品"RabbitAdvisors"。
3.1 业务场景分析
核心业务流程:
作者开设专栏
用户浏览专栏
用户付费订阅
用户阅读内容
公司与作者分成
知识付费领域
├── 订阅域(核心域)- 订阅、订单
├── 专栏域(支撑域)- 专栏、文章
├── 金融域(支撑域)- 分成、结算
├── 用户域(通用域)- 用户、权限
└── 评论域(支撑域)- 评论、点赞
3.3 限界上下文划分3.4 聚合设计示例┌─────────────────────────────────────┐
│ 专栏订阅上下文(微服务1) │
│ ┌─────────┐ ┌─────────┐ │
│ │ 订阅域 │ ←─── │ 订单域 │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────┘┌─────────────────────────────────────┐
│ 专栏管理上下文(微服务2) │
│ ┌─────────┐ ┌─────────┐ │
│ │ 专栏域 │ ←─── │ 文章域 │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────┘
以"订阅聚合"为例:
订阅聚合
│
├── 订阅(聚合根)
│ ├── 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 聚合设计原则
一致性边界:聚合内的对象必须保持一致性
通过聚合根访问:外部只能通过聚合根访问聚合内对象
小聚合优先:聚合越小越好,降低并发冲突
最终一致性:聚合之间通过领域事件保持最终一致
限界上下文是微服务拆分的天然边界:
六、DDD不是银弹一个限界上下文 ≈ 一个微服务划分原则:
- 支持完整的业务流程
- 团队自主性
- 独立部署能力
DDD也有其适用边界:
适合DDD的场景:
业务复杂、规则多变
团队规模较大
领域专家参与度高
期演进的产品
不适合DDD的场景:
简单CRUD系统
快速原型验证
小团队、短期项目
技术驱动而非业务驱动
DDD的本质不是技术,而是用业务视角思考和设计:
战略设计告诉你系统该怎么拆
战术设计告诉你代码该怎么写
统一语言让团队说同一种话
领域模型是业务知识的沉淀
记住:DDD是方法论,不是框架。它提供的是一种思考方式,帮助你更好地理解和建模复杂业务领域。
本文代码示例可在以下仓库找到: 后端微服务:github.com/eyebluecn/smart-classroom-misc 前端项目:github.com/eyebluecn/smart-classroom-front 在线演示:classroom.eyeblue.cn
热门跟贴