文章目录
DDD的概念
DDD(Domain-driven design),即领域驱动设计,是一种软件设计方法。也就是说 DDD 是指导我们做软件工程设计的一种手段,它提供了用切割工程模型的各类技巧。它的核心是将业务复杂性问题作为设计工作的核心,通过建立有效的领域模型来应对复杂性。
💪DDD的两大支柱: 战略设计 & 战术设计 战略设计解决"做什么"和"如何划分"的问题,战术设计解决"怎么做"的编码实现问题。
- 战略设计:主要以应对复杂的业务需求,通过抽象、分治的过程,合理的拆分为独立的多个微服务,从而分而治之。
- 战术设计:在这个范畴下,主要以讨论如何基于面向对象思维,运用领域模型来表达业务概念(可以理解为设计模式)。
简单来说,DDD 就像一位城市规划师。战略设计 是划分城市的功能区(住宅区、商业区、工业区),并定义它们之间的道路(上下文映射)。战术设计则是在每个功能区(如住宅区)内,规定房屋(实体)、街道(值对象)、小区(聚合)应该如何建设,以及物业(领域服务)的职责是什么。这样,整个城市才能井井有条,高效运转。
充血模型
介绍
充血模型 是指将数据 和操作这些数据的业务逻辑封装在同一个领域对象中的设计方式。
优势:
- 这样的方式可以在使用一个对象时,就顺便拿到这个对象的提供的一系列方法信息,所有使用对象的逻辑方法,都不需要自己再次处理同类逻辑。
- 但不要只是把充血模型,仅限于一个类的设计和一个类内的方法设计。充血还可以是整个包结构,一个包下包括了用于实现此包 Service 服务所需的各类零部件(模型、仓储、工厂),也可以被看做充血模型。
- 同时我们还会再一个同类的类下,提供对应的内部类,如用户实名,包括了,通信类、实名卡、银行卡、四要素等。它们都被写进到一个用户类下的内部子类,这样在代码编写中也会清晰的看到子类的所属信息,更容易理解代码逻辑,也便于维护迭代
贫血模型示例
java
// 只有数据,没有行为 - 纯粹的数据容器
public class Order {
private Long id;
private BigDecimal amount;
private String status;
private List<OrderItem> items;
// 只有getter/setter,没有业务方法
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
}
// 业务逻辑都分散在Service中
@Service
public class OrderService {
public void placeOrder(Order order) {
// 验证逻辑
if (order.getAmount() == null || order.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("订单金额必须大于0");
}
// 状态设置逻辑
order.setStatus("PENDING");
// 保存逻辑
orderRepository.save(order);
}
}
充血模型示例
java
// 富含业务行为的领域对象
public class Order {
private Long id;
private BigDecimal amount;
private OrderStatus status;
private List<OrderItem> items;
// 构造函数封装创建逻辑
public Order(BigDecimal amount, List<OrderItem> items) {
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("订单金额必须大于0");
}
this.amount = amount;
this.items = new ArrayList<>(items);
this.status = OrderStatus.PENDING;
}
// 业务方法:下单
public void place() {
validateOrder(); // 内部验证
this.status = OrderStatus.CONFIRMED;
// 可以在这里发布领域事件:new OrderPlacedEvent(this)
}
// 业务方法:取消订单
public void cancel() {
if (this.status != OrderStatus.PENDING && this.status != OrderStatus.CONFIRMED) {
throw new IllegalStateException("当前状态不能取消订单");
}
this.status = OrderStatus.CANCELLED;
}
// 业务方法:计算总价
public BigDecimal calculateTotal() {
return items.stream()
.map(OrderItem::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
// 内部验证方法
private void validateOrder() {
if (items == null || items.isEmpty()) {
throw new IllegalStateException("订单必须包含商品");
}
}
// getter方法(没有setter,通过业务方法修改状态)
public Long getId() { return id; }
public BigDecimal getAmount() { return amount; }
public OrderStatus getStatus() { return status; }
}
总结
🖊️总结: 充血模型是DDD战术设计的灵魂,它让领域对象从被动的"数据袋子"变成主动的、有行为的业务实体。这种设计方式让代码更符合面向对象思想,也更容易维护和演进。
领域模型有如下概念:
- 领域服务
- 领域对象
- 仓储定义
- 事件消息
- 端口适配器

⭐⭐⭐
领域模型还有一个特点,它自身只关注业务功能实现,不与外部任何接口和服务直连。如;不会直接调用 DAO 操作库,也不会调用缓存操作 Redis,更不会直接引入 RPC 连接其他微服务。而是通过仓库和端口适配器,定义调用外部数据的含有出入参对象的接口标准,让基础设施层做具体的调用实现------通过这样的方式让领域只关心业务实现,同时做好防腐。
领域模型层次:
- 实体 (有身份,有生命周期)
- 值对象 (无身份,描述性)
↓- 聚合 (一致性边界)
├── 聚合根 (入口点)
└── 聚合内部对象
↓- 限界上下文 (业务边界)
├── 领域服务 (跨聚合逻辑)
├── 领域事件 (业务事实)
└── 仓储接口 (持久化抽象)