大家好,我是G探险者!
今天来聊一聊DDD。
在软件开发的世界里,架构从来不是一开始就复杂的。
绝大多数系统,最初都只是简单的 CRUD 系统。但随着业务的增长,系统复杂度不断增加,原有的开发模式逐渐暴露出问题。于是,新的架构思想开始出现。
DDD(Domain-Driven Design,领域驱动设计)正是在这样的背景下诞生的。
理解 DDD 的最好方式,并不是直接学习那些复杂的概念,而是从软件架构的演进过程来看:
为什么我们最终需要 DDD。
一、第一阶段:CRUD时代 —— 数据库驱动开发
在企业软件早期,大多数系统都是围绕数据库构建的。
典型架构结构:
Controller → Service → DAO → Database
系统本质上是:
页面 + 表 + SQL
开发流程通常是:
- 设计数据库表
- 编写 SQL
- 提供接口
- 页面展示数据
这种模式的核心特点是:
- 数据库是系统中心
- 业务逻辑很薄
- 系统主要是增删改查
这种开发方式就是我们熟悉的:
CRUD(Create Read Update Delete)模式
这种模式非常适合:
- 管理后台
- CMS系统
- 配置系统
- 权限系统
直到今天,大量企业系统仍然采用这种模式。
但当业务复杂度上升时,问题开始出现。
二、第二阶段:三层架构 —— 软件工程化
随着系统规模扩大,软件工程开始强调结构化设计,于是出现了经典的 三层架构:
表现层(Controller)
业务层(Service)
数据层(DAO)
职责划分:
| 层 | 职责 |
|---|---|
| Controller | 接收请求 |
| Service | 业务逻辑 |
| DAO | 数据访问 |
这种架构带来的改进:
- 代码结构更清晰
- 业务逻辑与数据访问分离
- 更利于团队协作
三层架构成为 Java 企业开发的主流模式。
但随着系统规模继续扩大,一个新的问题逐渐出现。
三、第三阶段:Service 膨胀
在三层架构中:
- Controller 很薄
- DAO 只负责数据库
于是,所有业务逻辑都堆到了:
Service层
随着业务增加,Service层逐渐变成:
状态判断
流程控制
业务规则
外部系统调用
事务控制
日志处理
异常处理
最终形成所谓的:
Fat Service(肥胖服务层)
一个 Service 方法可能:
- 几百行代码
- 大量 if-else
- 多个系统调用
Service逐渐变成:
业务逻辑的大泥球
四、第四阶段:贫血模型
与此同时,系统中的领域对象往往非常简单。
例如一个订单对象:
class Order {
Long id;
Integer status;
BigDecimal amount;
}
它通常只有:
- getter
- setter
而业务逻辑却写在 Service:
orderService.pay(order)
orderService.cancel(order)
orderService.refund(order)
这种模式被称为:
贫血模型(Anemic Domain Model)
问题是:
- 对象只有数据,没有行为
- 业务规则散落在各个 Service
- 代码难以理解
- 系统难以维护
随着系统复杂度增加,这种问题会越来越严重。
五、第五阶段:业务复杂度爆炸
当系统进入核心业务阶段时,复杂度会快速增长。
例如一个订单系统,可能涉及:
- 下单
- 支付
- 发货
- 退款
- 售后
- 库存锁定
- 营销活动
- 价格计算
各种业务规则:
已支付订单不能取消
库存不足不能下单
优惠券规则
会员等级规则
退款时间限制
如果这些规则散落在:
- Service
- SQL
- Controller
系统很快会变得:
- 难以理解
- 难以维护
- 难以扩展
这时开发者往往会发现:
功能可以做,但系统越来越难改。
六、DDD的出现:业务驱动架构
在这样的背景下,Eric Evans 在 2003 年提出了:
Domain-Driven Design(领域驱动设计)
DDD的核心思想是:
软件设计应该围绕业务领域进行,而不是围绕技术实现。
传统架构关注:
数据库
接口
框架
DDD关注:
业务模型
领域概念
业务规则
系统边界
简单来说:
| 传统架构 | DDD |
|---|---|
| 技术驱动 | 业务驱动 |
| 数据库中心 | 领域模型中心 |
七、DDD核心思想
DDD包含几个关键概念。
1 领域模型
系统应该围绕业务模型设计。
例如订单系统核心对象:
订单 Order
订单项 OrderItem
支付 Payment
优惠 Coupon
领域对象不仅有数据,还应该有行为:
order.cancel()
order.pay()
order.refund()
业务规则应该由领域对象负责。
2 统一语言(Ubiquitous Language)
DDD强调:
开发人员和业务人员应该使用统一的业务语言。
例如:
- 订单
- 库存锁定
- 支付确认
- 退款申请
这些概念应该直接体现在代码中。
这样代码就能表达业务。
3 限界上下文(Bounded Context)
在复杂系统中,同一个概念在不同系统中可能含义不同。
例如“订单”:
| 系统 | 含义 |
|---|---|
| 交易系统 | 用户购买记录 |
| 仓储系统 | 发货任务 |
| 财务系统 | 结算凭证 |
DDD提出:
每个业务上下文拥有自己的模型
这就是:
Bounded Context(限界上下文)
八、DDD与微服务
随着微服务架构的发展,人们发现:
DDD提供了一种非常自然的服务拆分方式。
因为:
Bounded Context ≈ Microservice
例如:
订单上下文 → Order Service
库存上下文 → Inventory Service
支付上下文 → Payment Service
DDD帮助架构师回答一个关键问题:
服务应该如何拆分?
九、DDD的真正价值
很多人认为 DDD 只是:
- 多几个类
- 多几层结构
其实并不是。
DDD真正解决的是:
复杂业务系统如何组织
它帮助架构师:
- 理解业务
- 划分系统边界
- 管理业务复杂度
- 支持系统长期演进
十、架构演进时间线
从架构演进角度看,软件开发经历了这样一条路径:
CRUD开发
↓
三层架构
↓
Service膨胀
↓
贫血模型
↓
业务复杂度爆炸
↓
DDD出现
↓
领域建模
↓
Bounded Context
↓
微服务架构
DDD并不是为了让系统变复杂,而是为了:
用更好的模型管理复杂业务。
十一、结语
从架构演进的角度来看,DDD的意义在于:
让软件系统真正反映业务世界。
当系统结构与业务结构一致时,软件才能具备:
- 可理解性
- 可维护性
- 可演进性
这正是领域驱动设计的核心价值。