学习视频来源:DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid=1940048&ctype=0
文章目录
- 关系型数据库的数量关系
- 领域模型的数量关系
- 实现聚合数量关系
- 聚合内
- 聚合间
- 具体说明
- 代码
- 数量关系是本质吗?
- 领域对象之间的数量关系是不变的吗?
- 为什么领域对象之间存在这样的数量关系?
关系型数据库的数量关系
在设计关系型数据库的时候,我们会设计很多的表,每个表都是一个实体。表和表之间或者说实体和实体之间数量可能会存在某种约束,比如一对一、一对多、多对多。
领域模型的数量关系
在面对象的设计的时候,对象和对象之间也会存在这种数量关系。因为在领域驱动设计中,是采用面向对象的方法设计领域模型,所以领域模型中也会具有这种数量关系,而且这个数量关系很重要。不过我们会更关心它是如何产生的这种数量关系,而不只是关心它们之间的数量关系是什么。
实现聚合数量关系
在实现的时候,聚合内和聚合间的数量关系是存在很大差别的。
聚合内
- 导航采用对象引用实现。因为在同一个聚合内,所以需要同时载入内存,同时持久化。
- 尽量隐藏聚合内部对象的数量关系
通过定义函数,维护数量关系,不要直接把暴露出去让外部操作
聚合间
- 导航采用持有id实现。避免载入A聚合时导致载入了B聚合。
具体说明
up主视频里说的有点抽象了,我这里画了张图具体说明一下。如下图,聚合内会有多个对象,也就是图中的聚合根、实体、值对象。订单作为聚合根,直接持有和它在同一个聚合内的对象引用。而订单和商品作为不同的聚合,订单持有的是商品聚合根的唯一标识,比如商品id。
代码
@Data
public class Order {
private Long orderId; // 订单id
// 聚合内
private List<OrderItem> orderItemList;
private Money money;
private Address address;
// 聚合间
private List<Long> productIdList; // 商品id列表
// 通过构造函数,使得必要的对象可能同时载入内存
public Order(Long orderId, Money money) {
this.orderId = orderId;
this.money = money;
}
// 通过定义函数,维护数量关系,不要直接把暴露出去让外部操作
public void addOrderItem(List<OrderItem> orderItems) {
if (orderItems.size() > 10) {
throw new RuntimeException();
}
// ...
// ....
}
}
@Data
public class Product {
// 聚合内
private List<ProductDetail> productDetailList;
private Color color;
private Comment comment;
private Size size;
}
数量关系是本质吗?
领域对象之间的数量关系是不变的吗?
答案显然是否,它们之间的关系会随着业务场景发生变化。比如一个订单刚开始对应一次支付,但随着业务的发展,可能会变成一个订单可以支持多次支付,也有可能变成多个订单可以合并,被一次支付掉。
为什么领域对象之间存在这样的数量关系?
数量关系不是本质,功能才是。领域之间之所以是存在这样的数量关系,是因为这样的数量关系为了实现某个功能而特意设计的。当功能变更的时候,数量关系就可能发生变化。
所以我们在设计领域模型的时候,不会一上来就去谈两个领域对象之间的数量关系,我们更关心在哪个动作、哪个命令执行的时候,它需要在这个时刻从一个领域对象,找到另外一个领域象。怎么找?找了多少个?是关心这个具体场景下的这些问题。所以我们是以业务功能作为切入点的。如果在这个功能需要,我们就把这个关系数量加上;如果这个功能不需要,我们就不加,因为加了没有意义。不仅是数量关系,聚合内持有的哪些数据也是由功能决定的。领域对象之间数量关系是一个结果,它可以帮助别人理解领域模型,但它不是领域对象之间存在这样的数量关系的原因。
这里我加个具体的例子,比如一个订单可能对应多次支付记录,如果没有功能需要使订单和支付关联起来,我们不需要维护二者的关系,不需要在订单中持有支付记录的引用或者id列表。但是如果新加了一个功能,比如需要支持查看某个订单下关联的支付记录,这样二者就发生了联系,那么这时候我们就需要加上这个数量关系,让订单持有支付记录的引用或者id列表。
可以看到,领域对象是否存在这样的数量关系取决于功能是否需要。