Skip to content

13 迭代二概述:怎样更深刻地理解领域知识?

你好,我是钟敬。

这节课开始,我们即将进入第二个迭代,一起学习几个DDD里更高级的实践和模式,带你逐步提升领域建模能力,更深刻地理解领域知识。

在梳理迭代二主要内容和迭代需求之前,我会先帮你巩固一下迭代一知识点。这是因为,你在学习了前面的课程以后,有些知识点虽然掌握了,但知识点之间还没能形成有机的整体。这节课我会帮你把它们联系起来,形成完整的知识图景。另一方面,只有扎实地掌握迭代一,才能更好地掌握迭代二。

迭代一回顾

在《领域驱动设计》的原书里,Evans喜欢用“概念图”总结模式。我也准备学习原作者,用概念图帮你串联一下迭代一的知识点。概念图是个比较简单的工具,在下面的讲解过程中你自然就会掌握了。另外,我也会指出每个知识点在《领域驱动设计》一书中的章节,以便你对照原书,进一步加深理解。

按照建模落地的过程,我们把迭代一的知识点分成捕获行为需求和事件风暴、模型的建立以及模型的实现这三个部分来回顾。

捕获行为需求和事件风暴

首先,我们复习一下捕获行为需求和事件风暴。下面这张图表示了捕获行为需求和DDD两个核心模式的关系。

这张图就是一张概念图。一个椭圆框表示一个概念。箭头表示概念之间有关系。箭头上的文字表示关系的含义。箭头的方向代表的是读这些文字的方向。比如说,“用例分析”指向“捕获行为需求”,读“……是……的一种方法”的时候,就把“用例分析”代入第一个省略号,把“捕获行为需求”代入第二个省略号,对应的读法是“用例分析是捕获行为需求的一种方法”。

另外,橙色的椭圆代表《DDD》原书中的模式。黄色椭圆的概念不是《DDD》中的模式。可能在书里提过,但没有算作模式,也可能书中没有提,而是我们课程补充的内容。

DDD有“统一语言”和“模型驱动设计”两个核心模式。统一语言在书里的2.1节;模型驱动设计在3.1节。

上面这张图的意思是,捕获行为需求为模型驱动设计奠定了基础。在捕获行为需求的时候已经开始形成统一语言,在模型驱动设计的过程中会进一步完善统一语言。

捕获行为需求有不同的方法,我们这门课重点介绍了事件风暴,但不意味着别的方法不可以,例如用例分析、用户故事地图也是常见的方法。即使你的团队已经有了成熟的方法,事件风暴的思路也仍然值得借鉴,你可以尝试在原来的方法里加入协作、统一语言、识别领域名词的实践。

下面我们再用一张概念图回顾一下事件风暴内部的元素。你也可以学以致用,演练一下这张图里的内容怎么读。

从这张图里我们看到了这样的概念和关系:执行者查询读数据,然后发出命令。命令触发领域事件。可以从命令、领域事件、执行者、读数据中识别出领域名词。

领域事件也是一种DDD的模式,但在2003年的《DDD》一书里没有提过这一模式,但后来业界的一些专家提出了领域事件的重要性。直到2015年,Evans又写了一本小册子 《DDD Reference》 来简要地总结DDD的所有模式。这时候才在DDD中正式增加了这个模式。

下面这张图说明了事件风暴的元素和DDD其他部分的关系。

我们按从上往下,从左往右的顺序来解读这张概念图。

首先,读数据和命令,一般会映射到应用服务的API。读数据映射为查询功能,命令映射为增、删、改等功能。然后,应用服务会查询或操作领域对象,领域对象的修改则会触发领域事件。最后,领域对象的识别,可以从领域名词开始。执行者,一般会映射成领域对象充当的角色。

模型的建立

下面我们来复习模型的建立,我们结合后面这张图来看看。

相比之前的概念图,是不是感觉这张图有点眼花缭乱了?其实如果你充分理解了迭代一的知识,就会觉得容易阅读了。

我们从领域知识开始看。DDD非常重视对领域知识的消化,这主要是在模型驱动设计过程中进行的。而领域模型是模型驱动设计的核心。领域模型使用UML来建模,包含表示领域概念的领域对象、对象之间的关联以及把对象按内聚和耦合关系分组的模块。领域对象分成实体和值对象两类,不过迭代一只讲了实体,所以说实体是一种领域对象。

另外,领域知识还包含业务规则,把业务规则记录在业务规则表中,可以保证业务规则在分析和实现的时候不会遗漏。

在模型驱动设计的过程中会运用和形成统一语言,统一语言用于描述领域知识,而领域模型和词汇表都可以帮我们固化统一语言。

在《DDD》原书的第一章讲了如何消化领域知识;5.1节讲了关联,在5.2节讲了实体,5.4节讲了模块。

模型的实现

说完模型的建立,我们再看看模型的实现。可以先看看下面的图。

我们从“领域模型”开始看。模型的实现主要是依据模型编写代码和设计数据库。代码和数据库的命名都要依据统一语言。编码时使用分层架构。

分层架构主要包括 领域层、应用层、适配器层,我们分别细看一下这三层所关联的主要概念。

  • 领域层中的领域对象和模块都与领域模型保持一致。领域对象和领域服务都会实现领域逻辑。此外,领域层还有工厂来创建领域对象,仓库的接口来持久化领域对象。
  • 应用层主要包含应用服务,是领域层的“门面”。
  • 适配器层用于分离输入输出技术和业务关注点。适配器分为主动适配器和被动适配器,其中被动适配器包含仓库的实现。

最后,不论是领域对象、领域服务还是应用服务,都应实现揭示意图的接口。

在《DDD》原书的4.1节介绍分层架构;5.4节介绍服务,包含了应用服务和领域服务;6.2节介绍工厂;6.3节介绍仓库;10.1节介绍揭示意图的接口。

总的来说,通过迭代一,我们掌握了最基本的技能,回顾完迭代一的内容,我们再讲一下迭代二会讲什么新内容。

迭代二的内容

通过迭代二,我们会掌握一些更高级的技能,以便处理更复杂的需求。这几个技能都是在DDD中不太好理解的,分别是聚合、值对象、限定和泛化。

学完了迭代一,在此基础上讨论这些进阶知识,你才更容易消化理解。

第一个要讲的是“聚合”。聚合是若干个具有整体部分关系的领域对象的组合。而且,需要维护有强一致的业务规则。聚合能帮助我们更简单地确定事务边界,保护业务规则。

第二个是“值对象”。我们在迭代一讲过,领域对象分成实体和值对象两种。实体比较好理解,而值对象的含义就众说纷纭了。我们在这个迭代会从建模、编码、背后的认识事物的思维等方面把值对象讲清楚。理解了值对象,可以更细致地为领域知识建模,避免一些建模中常见的错误,而且也可以促使我们更深刻地理解实体。

接下来,我们会讲“限定”。这个技术掌握起来并没有想象中难,而且很有用,可以起到深化领域知识和简化关联的作用。《DDD》一书中也做了强调。

最后一个是“泛化”。泛化是建模技能由初级向中、高级进阶的关键技能,可以帮我们进一步提高模型的抽象程度。恰当使用的话,会使模型变得更加灵活和强大。当然,我也会介绍什么时候“不”使用泛化。

迭代二的需求

为了讲解前面这几个技能,我们在迭代二里会引入几个新的需求。这些需求中的大部分,都是由于SaaS系统要应对不同公司需求,所增加的灵活性。

需求一:人员与组织管理

首先看看人员和组织管理的新需求。

在这个迭代,我们要为员工记录工作经验和技能。一个员工可以有多条工作经验,也可以有多种技能。

记录工作经验时,要记录工作的公司和时间段。例如,小张在2001年1月1日到2005年1月1日在某某银行工作。

记录技能时,要记录一个人在某个技能上积累了多少年的经验,达到什么程度。比如,小王积累了10年的Java经验,达到高级水平。

此外,还会有一些相应的业务规则,比如:工作经验的时间段不能重叠;一个技能不能录入两次;时间段的结束日期要大于开始日期等。

需求二:项目管理

在项目管理方面也新增了几个需求。

第一,为了在管理上评估绩效和分清责任,需要记录客户经理、合同经理、项目经理的变更历史。例如小张在今年上半年担任项目A的项目经理,下半年转去做另一个项目B的经理。那么,年终评价时,小张上半年在A项目上的成绩仍然属于小张,不会因为小张现在是项目B的经理就不算了。如果不记录历史,就无法做到这一点。

第二,为了更细粒度的管理项目,需要把项目拆分为子项目。

第三,有些公司希望把项目分成客户项目和内部项目两种。客户项目是服务于外部客户的,必须有合同。内部项目是公司内部的,没有合同;

第四,还有一些公司,想要把项目分成需要分配人员的项目和不需要分配人员的项目。比如有些公司内部兴趣小组的项目,可以不必专门分配人员。

需求三:工时登记

上面的变动会影响到工时登记。比如说,有些公司需要在项目层面报工时,另外一些公司需要在子项目上报工时,还有些公司则是“两可”。

业务规则也有相应的变化,比如说,只有对于需要分配人员的项目,才会适用“员工只能在已分配的项目上报工时”的规则。

另外,还有些工时,不报在任何项目上。比如说,项目不紧张的时候,员工可以用一些时间学习。管理人员还经常需要一些纯粹的管理和沟通时间。这些时间都不属于某个特定的项目。这种报工时的需求这个迭代里也要得到处理。

最后,今天讲的所有需求我都汇总到了下面这张表里,你可以看一下,方便后面的实战。

总结

今天的内容就基本讲完了,我们来总结一下。

这节课,我们使用 概念图 的方式回顾了第一个迭代的内容,包括捕获行为需求和事件风暴、模型的建立和模型的实现三部分。我们还区分了哪些内容是《DDD》原书中的模式,并且给出了原书中对应的章节号,以便你对照阅读,加深理解。

顺便说一句,学习一门学问,阅读“原典”往往是学习的“捷径”。但是由于一些小伙伴面向对象的基础不牢,所以觉得《DDD》原书“晦涩”,不容易懂。学了我们这门课程再看原著,你可以体会一下,会不会有一种豁然开朗的感觉。

今天我们还概述了迭代二的主要内容,包括聚合、值对象、限定和泛化。这些概念常常众说纷纭,以讹传讹。我会在迭代二尝试用通俗易懂的方式,为你揭示它们的本质意义,以便你能够更深刻地理解领域知识,处理更复杂的问题。

为了帮你更好地消化这些新内容,我们在这个迭代里引入了一些新需求。针对这些新需求,你不妨自己先想想怎样建模,怎样写代码。在后面的学习过程中,再对比一下我们的课程是怎么讲的,这样你的理解会更加深刻。

思考题

最后是两道思考题。

1.你以前是否听说过聚合,你是怎么理解的?

2.你以前是否听说过值对象,你是怎么理解的?

好,今天的课程结束了,有什么问题欢迎在评论区留言,下节课,我们开始学习一个重要的DDD模式——聚合。