我们通过是否有状态来做区分 。
首先识别有状态的对象:活动、各种参与资格、权益、活动参与记录、用户 。一般有状态的对象都是事物,对应的构造块类型也就是实体或者值对象 。
其次判断其状态是否会改变:
- 活动会被修改,所以状态会被改变;
- 参与资格会被修改,但是参与资格从属于活动,修改后可以直接使用新的对象替换旧的,所以可以设计成状态不变;
- 权益和参与资格一样,也可以设计成状态不变;
- 活动参与记录,状态可能发生变化;
- 用户在这个模型中只是临时存在,状态不会变化 。
最后剩下的就是无状态的对象:活动通用规则 。对应的构造块类型是领域服务 。
这里的无状态对象大都可以转化成有状态的对象,例如活动通用规则,可以将方法参数的Optional<活动参与记录>变成成员变量 。只是这里我们选择了无状态的设计方法 。
由于领域服务没有状态,所以可以在应用启动时就创建出来,也可以在使用时才创建 。
文章插图
经过分析,我们的领域模型都有了类型 。
文章插图
设计聚合首先识别生命周期长的领域对象:在一个操作中被创建出来,操作结束后仍会被其他操作使用的对象 。活动、参与资格、权益和活动参与记录都是生命周期长的对象 。
其他有状态的对象都是临时对象:在一个操作中被创建出来,操作结束后就不会再被使用 。模型中的用户,在一次操作中从其他服务获取,使用后即被丢弃 。
这里我们总结下各构造块类型的特点:
值对象
领域服务
是否有状态
有且状态可变
有且状态不可变
无
生命周期
长
长或者短
长短均可
在生命周期的长的对象中,我们要设计聚合 。聚合作为操作单元,主要解决以下几个问题:
- 整个模型往往庞大复杂,为了降低知识负载,需要将其分解成多个小且简单的模型,划分清晰的边界
- 部分模型对象之间存在一致性规则,例如需要被一起删除,所以需要放在一个操作中
- 多个用户可能会并发操作模型,为了避免相互干扰,需要让操作单元尽可能小
- 对于操作单元,需要将其频繁加载到内存中,如果单元过大,往往不能满足性能要求
所以我们将活动、参与资格、权益设计成一个聚合,而活动参与记录作为一个单独的聚合 。而活动和活动参与记录分别作为这两个聚合的聚合根 。对应的,聚合都会配备其专属的Repository 。
文章插图
同时加上遍历方向箭头 。由于活动是聚合根,从活动可以遍历到聚合内部的参与资格和权益 。另外查询活动参与记录,可以通过其Repository,所以没有活动到活动参与记录的箭头 。
由于我们将活动和活动参与记录之间划分成不同聚合,那他们之间的关联将使用聚合的ID来关联,而不是聚合本身 。
PS:如果使用了关联对象,遍历方向也可以是从活动到活动参与记录 。
如何使用领域模型领域模型已经建立完毕,我们来看如何使用领域模型以满足用例 。
运营人员创建活动基本信息及其关联的参与资格和权益 。领域模型的客户(一般来说是应用服务),使用运营人员输入的参数构造出活动对象,再利用Repository将其保存 。
推荐阅读
- JavaScript 内存管理:如何避免常见的内存泄漏并提高性能
- 免费版cloudflare如何防CC攻击?不需要动手写规则!
- 通过源码探查HashSet究竟是如何保证唯一性的
- ChatGPT热潮下的冷思考:人工智能将如何影响人类的未来?
- C++如何创建窗口程序?Windows API永不过时
- 使用 Python 构建 Web 应用程序从未如此简单——从 Pynecone 开始
- 无需打开应用,如何用 api 实现在 Notion 的表格中写入数据
- 如何查看你的QQ注册时间和全球排名?附详细操作教程
- 如何成为优秀的工业设计师?
- 机器学习如何检测使用沙盒逃避和静态防护的恶意软件