如何在DDD中建立领域模型( 二 )


我们通过是否有状态来做区分 。
首先识别有状态的对象:活动、各种参与资格、权益、活动参与记录、用户 。一般有状态的对象都是事物,对应的构造块类型也就是实体或者值对象 。
其次判断其状态是否会改变:

  • 活动会被修改,所以状态会被改变;
  • 参与资格会被修改,但是参与资格从属于活动,修改后可以直接使用新的对象替换旧的,所以可以设计成状态不变;
  • 权益和参与资格一样,也可以设计成状态不变;
  • 活动参与记录,状态可能发生变化;
  • 用户在这个模型中只是临时存在,状态不会变化 。
状态会改变的是实体,包括活动和活动参与记录;状态不变的就是值对象,包括参与资格、权益和用户 。
最后剩下的就是无状态的对象:活动通用规则 。对应的构造块类型是领域服务 。
这里的无状态对象大都可以转化成有状态的对象,例如活动通用规则,可以将方法参数的Optional<活动参与记录>变成成员变量 。只是这里我们选择了无状态的设计方法 。
由于领域服务没有状态,所以可以在应用启动时就创建出来,也可以在使用时才创建 。
 
如何在DDD中建立领域模型

文章插图
 
经过分析,我们的领域模型都有了类型 。
 
如何在DDD中建立领域模型

文章插图
 
设计聚合首先识别生命周期长的领域对象:在一个操作中被创建出来,操作结束后仍会被其他操作使用的对象 。活动、参与资格、权益和活动参与记录都是生命周期长的对象 。
其他有状态的对象都是临时对象:在一个操作中被创建出来,操作结束后就不会再被使用 。模型中的用户,在一次操作中从其他服务获取,使用后即被丢弃 。
这里我们总结下各构造块类型的特点:
 实体
值对象
领域服务
是否有状态
有且状态可变
有且状态不可变

生命周期

长或者短
长短均可
在生命周期的长的对象中,我们要设计聚合 。聚合作为操作单元,主要解决以下几个问题:
  • 整个模型往往庞大复杂,为了降低知识负载,需要将其分解成多个小且简单的模型,划分清晰的边界
  • 部分模型对象之间存在一致性规则,例如需要被一起删除,所以需要放在一个操作中
  • 多个用户可能会并发操作模型,为了避免相互干扰,需要让操作单元尽可能小
  • 对于操作单元,需要将其频繁加载到内存中,如果单元过大,往往不能满足性能要求
根据对业务的了解,活动及参与资格、权益都是一起被创建和修改,可以放在一个聚合里;活动和活动参与记录之间没有一致性规则,可以分开;因为活动参与记录数量会很多,如果和活动在一个聚合中,会降低性能 。
所以我们将活动、参与资格、权益设计成一个聚合,而活动参与记录作为一个单独的聚合 。而活动和活动参与记录分别作为这两个聚合的聚合根 。对应的,聚合都会配备其专属的Repository 。
 
如何在DDD中建立领域模型

文章插图
 
同时加上遍历方向箭头 。由于活动是聚合根,从活动可以遍历到聚合内部的参与资格和权益 。另外查询活动参与记录,可以通过其Repository,所以没有活动到活动参与记录的箭头 。
由于我们将活动和活动参与记录之间划分成不同聚合,那他们之间的关联将使用聚合的ID来关联,而不是聚合本身 。
PS:如果使用了关联对象,遍历方向也可以是从活动到活动参与记录 。
如何使用领域模型领域模型已经建立完毕,我们来看如何使用领域模型以满足用例 。
运营人员创建活动基本信息及其关联的参与资格和权益 。领域模型的客户(一般来说是应用服务),使用运营人员输入的参数构造出活动对象,再利用Repository将其保存 。


推荐阅读