有没有完美的DDD落地方案呢?这取决于业务上下文,取决于实际的问题。
不同的业务系统,面临的业务挑战有所区别,架构也就各有不同,但是跟随着DDD的指导思想去落地实践,那么我们也就可以从中获取到DDD架构的益处,真正的给微服务注入灵魂了。
构建DDD架构,最核心的就是找出领域模型,以及区分领域的边界。为了确定领域模型,我们需要一个探索性的挖掘过程。
为了能够给业务领域建模,我们需要通过一些方法去探索,常见的方法有事件风暴建模,问题空间与解决空间,下面我们进行介绍。
1 问题空间与解决空间[1]
我们可以通过引入问题空间,进行设计得出解决空间,得到最终更多领域模型,如下图所示,首先是问题空间:
领域专家和研发团队一起基于要解决的问题和业务愿景进行讨论,并核对相关领域知识,在此过程中与业务专家建立一个统一语言,用于后续的沟通。
为什么要建立统一语言?
如果没有建立统一语言,那么接下来的需求讨论会产生很多歧义,而且极不利于新同事熟悉代码,因为在基于领域专家的语言了解了需求之后,发现代码里面又是另一种表达方式。
举个例子:
- 可能开发同同事没有在代码里面建立洗碗这个概念,于是每次要触发洗碗的时候,代码里面都是这样过程式的表达:拿起一个碗,加点洗洁精刷呀刷呀刷呀,冲一下水,滤干水…这就是我们所说的重复代码。而如果开发知道洗碗这个概念,就会把这个过程封装到一个方法里面,这样每次看到这个方法调用,就知道是洗碗了,不用阅读过程式的代码,这样也就增加了代码的可读性。另外,有了这个方法,我们也就更好的把这个方法划分到具体的问题子域中进行分析了;
- 一个香港来的程序员与内地来的领域专家探讨怎么做草莓蛋糕,但是这个香港的程序员每次提到草莓的时候,都称为"士多啤梨"(香港一般把草莓按英文发音称呼为士多啤梨),最终导致他们很难沟通下去。而当他们讨论球星的时候,领域专家根本不认识这个程序员口中的“朗拿度”、“施丹”、“碧咸”、“美斯”了,尽管这些都是赫赫有名的球星。当然要是用英文沟通就不存在这些问题了。
然后基于领域知识,把大的问题域进行细分,然后提取成为一个一个的子域,当细分到一定的程度后,会将问题限定在特定的边界内,这个边界内也就是我们所说的限界上下文,最终在这个边界内建立领域模型。DDD的领域就是这个边界内要解决的业务问题域。
而在这个过程中,有几种方法可以辅助我们建立领域模型,下面一一介绍。
2 事件风暴法
事件风暴(EventStorming),首次于2013年11月18日在Ziobrando’s Lair的博客中被提出。详细的博文大家可以移步阅读:Introducing Event Storming[2]。
事件风暴是一种基于研讨会的形式,用于快速找出软件程序领域中发生的事情,探索复杂的业务领域。结果以宽墙上的便利贴表示。业务流程“一涌而出(stormed out)”作为一系列域事件,这些事件表示为橙色。选择这个名称是为了表明重点应该放在领域事件上,并且该方法的工作方式类似于头脑风暴或敏捷建模的模型风暴。
事件风暴可以使我们能够在数小时而不是数周内提出完整业务流程的综合模型。研讨过程中,将提出问题的人和参与讨论问题的人组织到一起,通过简单的符号进行构建模型,期间不会使用复杂的UML,避免远离问题讨论的核心。
以下是大致的工作步骤:
- 邀请适合的人参与,提前准备好要讨论的问题;对于事件风暴研讨会来说,让合适的人在场非常重要。这包括知道要问什么问题的人(通常是开发人员)和知道答案的人(领域专家、产品负责人);
- 提供一个足够大的表述白板空间,避免因为没有足够可用更多空间来查看复杂的问题,而最终导致问题没有被正确分析;
- 从领域事件开始探索领域;
- 探索领域事件的起源,某些是UI操作的结果,我们可以用蓝色便签表示为命令,某些是外部系统处理事件的结果,我们可以使用紫色的便签进行表示;
- 寻找聚合,聚合是系统的一部分,他接收命令并且决定是否执行它们,从而产生域事件。
在白板上,我们一步一步的添加便签[3]:
- 创建领域事件,用橙色便签表示
- 添加引起域事件的命令,用蓝色便签表示
- 添加执行命令的actor
- 添加相应的聚合,用黄色便签表示
最终得到如下的视图:
(标签图片来源于:Event storming[3:1])
更进一步的,你可以探讨聚合内部的子域,如果发现冲突领域,这个时候可以进一步讨论术语不同解释的意图,最终达成一致,划清领域界限。
更多的参与对象表示方法:
- 🟠 领域事件:业务流程中发生的时间,用过去时表达;
- 🟡 用户:通过视图执行命令的人;
- 🟣 业务流程:根据业务规则和逻辑处理命令,创建一个或者多个领域事件;
- 🔵 命令:用户通过聚合视图执行的命令,导致创建领域事件;
- 🟡 聚合:一组域对象共同组成一个单一的业务单元;
- 🌸 外部系统:第三方服务提供商;
- 🟢 视图:用户与之交互以在系统中执行任务的视图。
当然,最终,你可以根据讨论结果,输出一个完整的思维导图或者表格,然后进行分工开发。表格可以类似如下格式:
业务域 | 领域模型 | 聚合 | 领域对象 | 领域类型(聚合跟/命令/应用服务/领域服务实体/值对象/仓储接口…) | 命名 |
---|---|---|---|---|---|
… | … | … | … | … |
更多实战实例参考:
- 事件风暴过程全体验-上篇. Retrieved from https://cloud.tencent.com/developer/article/1556397
- 事件风暴过程全体验-下篇. Retrieved from https://cloud.tencent.com/developer/article/1556400
- 领域驱动设计从0到1之事件风暴. Retrieved from https://www.jianshu.com/p/797d96c1faab
3 四色建模法
四色建模法(Object Modeling in Color)[4],首次由Peter Coad
, Eric Lefebvre
和Jeff De Luca
等人在 The Coad Letter[5] 的一系列文章中提出,后来发表在他们的著作 Java Modeling In Color With UML。
在无数的领域模型中,很明显的四种主要类型一次又一次的出现,尽管他们在不同的领域有不同改动名词,最终我们通过四种颜色来代表着以下四种类型的领域对象。
四色建模可以用这句话进行描述:某人(Party, PartyRole角色)在某个地点(Place, PlaceRole角色)用某个东西(Thing, ThingRole角色)做了某件事情(MomentInterval)。
3.1 🌸 时标型(moment-interval)对象
表示在某个时刻或某一段时间内发生的某个活动。使用粉红色表示,简写为MI。‘这种对象往往表示在某一个时间点,一次外界的请求,产生的一个对象。例如,下单操作产生的订单对象。
这些对象是系统的价值所在,所以也是最重要的一类对象,我们用粉红色表示。
这种对象一般都有一个生命周期的起止时间,以及一个唯一标识。
这类对象有两个特点:
- 事实不可变性,记录过去某个时间发生的事实;
- 责任可追溯性,记录管理者关注的信息。
比如,某个机构创建了一个户外活动,小马报名参加户外活动,最终产生事实:户外活动
、报名登记记录
,这个户外活动和报名登记记录就是一个时标型对象。
更进一步的,我们可以按照时间发展顺序,列出所有的时标对象。
3.2 🟢 PPT(party, place, or thing)对象
表示参与某个活动的参与方(Part)或者参与物(Thing),活动发生的地点(Place),用绿色表示。
这些对象往往是客观存在的,如人,地点,产品等,这些对象往往在Moment-interval
中扮演某个角色,比如同一个人,在学校就是一个学生或者老师,在运动场上,就是一个运动员或者教练。
比如,小马报名参加户外活动,这个户外活动客观存在的事物有:
- 参与方:用户,机构;
- 地点:户外活动地点,比如 深圳湾公园。
3.3 🟡 角色(roles)对象
角色代表在这个moment-interval
中涉及的人或者物的身份,用黄色表示,一般一个moment-interval
会有多个角色共同参与。
3.4 🔵 描述(description)对象
表示对PPT对象以及时标型对象的描述,体现为一组属性集合,使用蓝色表示。
这样,我们就可以设计出相关的UML类图了。
许多人觉得有色物体对大脑的模式识别部分很有吸引力。其他人则主张您可以用一叠四色便签卡或彩色便签开始建模过程。为此,我们把这四种类型的领域对象进行不同的着色。
有些观点认为,领域建模最终不过就是使用UML画类图罢了,直接把关系型数据表换成类图就可以了,类图和数据库表似乎是一样的。其实不然,数据库模型是静态的,而类图是动态的,同样的一个数据库结构,可以通过多种不同的对象模型去表示,通过不同的领域建模方法,就可以找出最合理的对象模型。
更多实战实例参考:
- 运用四色建模法进行领域分析. Retrieved from https://www.infoq.cn/article/xh-four-color-modeling/
- 从“四色建模法”到“限界纸笔建模法”. Retrieved from https://insights.thoughtworks.cn/paper-pen-modeling/
4 限界笔纸法
四色建模法,通过事件的发展顺序,把业务核心数据模型建好了。
更进一步我们可以基于限界笔纸法[6],进一步做一下建模工作:
- 划分限界上下文,划分子域,避免模型发展成大泥球架构;
- 确定聚合中的聚合根,保证数据完整性,并且统一通过聚合根访问管理内部实体,保证了清晰的职责范围;
- 降低模型复杂度?
主要做法就是:
- 给时标型对象分类,描述这些分类的业务价值,确定核心领域;
- 确定核心领域之间的依赖关系;
- 列出核心领域的详细数据;
- 选定一个合理的具有唯一标识的实体作为聚合跟;
- 如果一些属性总是一起出现,那么尝试提取他们,作为新的实体或者值对象。
详细的使用案例,参考:
References
DOMAIN-DRIVEN DESIGN. Retrieved from https://danysk.github.io/Course-Laboratory-of-Software-Systems/21-ddd-intro/#/ ↩︎
Introducing Event Storming. Retrieved from http://ziobrando.blogspot.it/2013/11/introducing-event-storming.html ↩︎
Event storming. Retrieved from https://en.wikipedia.org/wiki/Event_storming ↩︎ ↩︎
Object Modeling in Color. Retrieved from https://en.wikipedia.org/wiki/Object_Modeling_in_Color ↩︎
The Coad Letter. Retrieved from https://web.archive.org/web/20061006012428/http://www.coadletter.com/coadletter/ ↩︎
从“四色建模法”到“限界纸笔建模法”. Retrieved from https://insights.thoughtworks.cn/paper-pen-modeling/ ↩︎