这是DDD建模最难的部分(其实很简单)
本文书接上回《为了落地DDD,我是这样“PUA”大家的》 ,欢迎关注公众号(老肖想当外语大佬),加群、获取最新文章更新和DDD框架源码,视频和直播在B站。
https://mp.weixin.qq.com/s/DjC0FSWY1bgJyLPIND5evA
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240813115919797-1724995065.jpg)
什么是最重要的事
如果你认真读过前面的文章,那么一定知道我们的核心逻辑:领域驱动是一种价值观,这个价值观是:“领域(边界)”的明确是软件设计掌控复杂度最重要的事。
那么整个软件交付过程中,架构师的职责就是持续保持“需求”、“模型”、“代码”三者的边界范围明确且一致。再回过头看整个软件交付存在的意义,就是为了满足需求,因此本质上来说,“需求”的边界就决定了一切,那么结论就不言而喻了:
最重要的事,就是需求的边界(范围)。
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240811162744858-673217397.png)
什么叫边界明确
假如说我有两个需求A和B,那么我们建模的时候就有如下几种结果,大家感受一下,通常情况下,自己给出的结果是哪个选项?哪个选项又是最理想的?
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240811162824116-1233256654.png)
我相信大部分人都会认同,其中B和D是符合边界明确、需求与模型边界一致的原则的,而现实的情况大部分结果是A和C,各种各样的“join”充斥着系统的各个角落,一个典型的例子,就是用户-角色系统的设计。
用户-角色系统,通常会有这样几个关键需求:
- 创建用户
- 创建角色
- 为用户设置角色
- 查看用户有哪些角色
- 查看一个角色包含多少个用户
我们用最传统的设计方法来做,模型大体是这样的:
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240811162853501-1504957833.png)
这样的结果是不是对应到了前面选项A和C比较类似?因为“用户聚合”与“角色聚合”连线的存在,导致需求与模型的边界不一致。
而我们要做的,就是在满足所有需求的同时,消除打破边界的连线。
如何操作
首先我们分析上图,假如我们把下面几个需求先去掉:
- 为用户设置角色
- 查看用户有哪些角色
- 查看一个角色包含多少个用户
那么我们会得到一个符合边界明确原则的设计:
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240811162923307-1719923499.png)
然后我们再思考,下面两个需求,应该哪个聚合负责:
- 为用户设置角色
- 查看用户有哪些角色
答案很显然是“用户聚合”,那么我们可以得到下面的设计:
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240811162946806-1217856865.png)
这时你会疑问,如果没有“用户聚合”和“角色聚合”的连线,怎么设置用户有哪些角色呢?
问题的关键,就在这里,通常我们总是会把“关系表”在图中用一条线来表示,那如果我说,“用户聚合”有一个集合属性,叫做“用户角色”,你会认同吗?如果我们知道用户对象有一个集合属性叫“用户角色”,那么是不是上图就很合理?
如果顺着这个思路,我们再来看需求“查看一个角色包含多少个用户”,它应该由哪个模型来解决?我想你已经知道答案了,就是“用户聚合”,最终我们得到如下设计:
![](https://img2024.cnblogs.com/blog/62928/202408/62928-20240811163011043-1391572636.png)
到此,所有的需求可以满足,需求被划分为两个范围,分别对应两个模型。
为什么说它很难
如果你一直跟着我的思路,完成了上面的过程,那么你会发现,需求的边界不是客观存在的,而是我们主观的划分,这个划分的目的是为了在一个确定的范围内,能够解决这个问题。因为它是主观的,就不可衡量和判断,每个人都可以有自己的划分思路。另外它又是简单的,因为你可以像上图一样,这样划分边界,给出对应的模型解决它,就像在给自己家的袜子分配收纳盒一样简单。
所以,我常常叹息,关于领域驱动设计:
说它难,难的是做出取舍。
说它简单,是因为能明确知道取什么舍什么。