09、真实世界的映照-DDD聚合

106 阅读3分钟

聚合是一个过程

聚合是一个过程,把紧密相连的实体、值对象,组合成一个更大的的实体、值对象,这个过程,就叫聚合(aggregation)。

一辆完整的小型家用汽车,由轮子、底盘、发动机、车架、方向盘、车灯等组成,发动机又由曲柄连杆机构、配气机构、冷却系、燃料供给系、润滑系、点火系、启动系等组成。

把曲柄连杆机构、配气机构等这些零件装配起来,形成一个完整的发动机,这个过程就叫做聚合。

把发动机、轮子等这些零件装配起来,形成一台完整的汽车,这个过程也叫聚合。

image.png

而DDD的本质是对真实物理世界的建模,DDD所提出的概念术语,都可以从现实世界中找到参照。

因此,聚合是一个过程,把紧密相连的实体、值对象,组合成一个更大的的实体、值对象,这个过程,就叫聚合(aggregation)

聚合后的产物,可能是一个更大的实体,也可能是一个更大的值对象。

image.png

聚合是一把双刃剑

聚合是一把双刃剑,在键在于你如何使用。

试想一下,如果我们需要通过车子编号来查找一辆汽车,如果没有聚合,我们是不是得调用发起机实体获取发动机、调用底盘实体获取底盘、调用车架实体获取车架……,如果车子的零件越多,我们需要调用的实体就越多,这样子,就要求着我们必须要了解汽车的零件组成结构!

聚合可以达到屏蔽内部细节的目的。当汽车被做成聚合的时候,我们只需要将汽车编号传递给汽车聚合,就可以拿到汽车聚合对象,至于汽车聚合里面的发动机等零件是怎么装配到汽车聚合中的,这就是汽车聚合本身的职责了,我们无需关心。

又试想一下,如果需求是:获取某台汽车的发动机编号。

按照聚合的做法,得先获取到汽车这个实体,通过汽车这个实体才能获取到发动机实体,通过发动机实体才能获取到发动机编号。

这样的做法,在获取汽车实体的时候,同时将好多非关注的实体也获取到了,如车架、底盘等,造成程序运行性能极慢。

如果绕过汽车这个实体,直接取发动机这个实体,虽然性能会大幅度提升,但是这种做法有孛于实体,因为提供通过汽车编号获取发动机编号的行为方法,从物理世界上看,不应该是发动机实体需要提供的。

这种情况,可以通过领域服务来实现这种需求。

使用聚合的建议

1、聚合中的属性没有必要一次性全部加载到聚合中去,在需要用到的时候再加载聚合的具体属性。

比如汽车这个聚合,当需要获取发动机实体的时候,再执行加载发动机实体的逻辑。

public class Car { 
    // 车子编码 
    private String no; 
    // 发动机实体 
    private Engine engine; 
    // 底盘 
    private Chassis chassis; 
    // 获取发动机实体 
    public Engine engine() { 
        return EngineRepository.load(engineId); 
    } 
}

2、聚合的粒度应该最小化

比如汽车,聚合的粒度最小化到汽车这个级别,不要聚合到汽车4S店这种级别。

3、没有必要,不要使用聚合,请使用领域服务