00前情回顾
上一篇文章,我给出了识别限界上下文的过程与方法。不可否认,这一过程和方法仍然存在by experience的意味。读者如果没有按照这一过程实际操练一遍,恐怕还是会懵懵懂懂。
即便亲自动手,如果不知结果之对错,识别出错误了,茫不知错误原因,仍然无法掌握这套方法。这就是为何需要工作坊演练的原因。感谢多位读者提交了工作坊演练的成果,接下来我将对其进行点评。
点评时,我常常很期待读者交付的成果。如果完成质量好,说明我给出的方法具有较好的参考价值,易于掌握;如果完成质量差,则相当于提供了反面教材,给出修改意见,就能让大家尽量避免陷入误区。
不可否认,要发挥工作坊演练的效果,最佳方式还是应该分小组讨论,通过充分交流,才能彼此取长补短,输出最佳答案。读者提交的演练成果都是由一个人完成,自然难免有疏漏之处。
01业务相关性
识别限界上下文的第一步是按照业务相关性对业务服务进行归类和归纳,归纳后得到的内容可以将其称之为“业务主体”或者候选的限界上下文。业务相关性可以认为是领域维度的高内聚低耦合划分原则。
01问题1:技术实现影响边界判断
识别限界上下文时,我们一定要注意识别的顺序:
- 领域维度
- 技术维度
- 团队维度
这个顺序不能乱!之所以名为“领域驱动设计”,就在于它将领域作为设计的驱动力,因此,首要影响限界上下文边界的,不是技术维度,也不是团队维度,而是领域维度。
下图是一位读者识别的两个限界上下文:
显然,这两个限界上下文是技术维度输出的结果。因为站在how to do的角度,它确实需要完成和微博与Github两个外部系统(我的书中将其称之为伴生系统)的集成。
让我们切换一下视角,单从领域维度来思考这两个业务服务:
- 获取微博动态
- 获取GitHub账户最新提交记录
虽然二者没有语义上的相关性,但从功能相关性角度看,二者其实提供了一个共同的业务目标:为用户提供更为丰富的个人信息。不要让技术实现干扰你的判断,显然,这两个业务服务更适合放在“用户上下文”。
02问题2:动词作为业务相关性判断依据
业务服务由动词短语构成。如果动词短语包含了名词类型的宾语,则该名词往往体现了领域概念,可以作为语义相关性的判断标准。下图所示的限界上下文就是通过语义相关性获得的:
如果要使用业务服务的动词进行判断,切忌不要以动词的语义相关性进行判断。下图给出的限界上下文恰恰犯了这个错误:
文章和部落上下文的获得确实符合语义相关性,而部落推荐、部落搜索与文章推荐上下文的获得,依据的就是动词相关性了。这一划分显然是不合理的。下图所示的划分结果犯了同样的错误:
真要以动词相关性来划分限界上下文,那倒是简单了。所有的业务功能无外乎增删改查,难道我们该由此将每个系统都划分为增、删、改、查四个限界上下文吗?
判断动词的相关性,考虑的其实是它对应的领域行为向外输出的业务能力,也就是功能相关性需要判断的业务目标。
例如下图的直播上下文:
为何要将以下没有任何语义相关性的业务服务放在同一个直播限界上下文?
- 打开摄像头
- 关闭摄像头
- 共享屏幕
- 转移主持人
- 发言
- 打赏
不还是因为它们都是在为直播功能服务吗?
根据动词相关性识别限界上下文的错误还有不少例子,例如一位读者识别出来的审批上下文与关注列表上下文,都是不合理的:
在归纳业务服务的共同特征时,应以名词作为候选限界上下文的名称。如果名称为动词,或动词形式的名词,就需检查和判断:我们是否错误地以动词作为业务相关性的判断依据了。
02亲密度
根据业务相关性的强弱对业务服务进行归类,实则遵循了“高内聚低耦合”原则。如果分配合理,必然遵循:同一限界上下文内业务服务之间的亲密度一定要高于跨限界上下文间的业务服务。若违背这一规律,说明业务服务的归类存在问题。检查下图分配的业务服务:
毫无疑问,属于账户信息上下文的“查询积分”业务服务,与同一限界上下文其他业务服务的亲密度,显然不及它与礼品上下文业务服务之间的关系强度。
03限界上下文的本质特征
根据业务相关性对业务服务进行归类,不过是一种分类的方法,如果只限于此,划分出来的不过是按照功能进行分解的模块罢了。既然是识别限界上下文,自然要从限界上下文的本质特征着手。
如一文所述,限界上下文的本质特征为:
- 领域模型的知识语境
- 业务能力的纵向切分
在检查我们识别出的限界上下文时,需要结合这两个本质特征作进一步甄别。
01领域知识的知识语境
语义相关性体现了对领域知识的归类,然而限界上下文不仅如此,它还是领域概念的边界,通过它可以限定当前上下文的领域知识,避免领域概念在理解上的分歧与冲突,形成当前上下文的统一语言。
例如在技术部落平台中,用户、企业用户和会员有着不同的知识语境。其中,企业用户与之对应的是个人用户,它们都是用户的一种类型,在对用户进行身份管理时,应该一视同仁,区别只在于用户的属性不同,应该将它们都放在用户上下文。至于会员,表示该用户属于部落的会员,实际体现的是用户与部落之间的关系,与之相关的业务服务应该放在部落上下文。
有的读者将用户上下文命名为账户上下文,这没有问题,但需要明确统一语言。例如账户代表的含义?不要将代表用户身份的账户与支付相关的账户混为一谈。
02业务能力的纵向切分
分配在一起的业务服务应该对外提供相同或相似的业务能力。以下图为例:
用户上下文对外提供的应该是用户身份认证与用户基本信息管理的业务能力。放在该限界上下文内部的以下业务服务则不然:
- 设置工作经历
- 设置项目经历
- 设置技能信息
- 生成个人简历
它们为个人简历提供了必要的领域知识,形成了与求职相关的业务能力。这些领域知识也只有在求职或招聘时才会使用到,因此,以上分配并不合理。
图中“关注活动”业务服务的分配也不合理,它主要违背了“亲密度”的原则,因为该业务服务与活动上下文的亲密度更高。
04验证原则
识别的限界上下文是否正确,分配的业务服务是否合理,还需要进一步通过我总结的验证原则进行验证。
01原则1:单一抽象层次原则
如果识别合理,获得的限界上下文应该尽可能处于同一个抽象层次。不同层次的抽象可能导致限界上下文的含义互相包含。例如:
即使部落推荐上下文和部落搜索上下文的识别是合理的,它也违背了单一抽象层次原则,因为部落上下文的抽象实际上已经涵盖了部落推荐和部落搜索的含义。
遵循单一抽象层次原则,还意味着我们在归纳业务服务时,需要适度的抽象,千万不要抽象过猛。下图归纳获得的限界上下文都是抽象过猛的反例:
社交、工具、服务、互动、售后都是含义极为宽泛的通用概念,矛盾的是,如此抽象的限界上下文,却仅仅包含了数量极少的业务服务,如此大的反差,足以说明抽象层次的不合理。
有时候,又可能因为抽象不够导致概念的不一致。如下图所示:
虽然抽象为招聘上下文,但其中包含的业务服务一部分提供了招聘的业务能力,另一部分则提供了求职的业务能力。显然,招聘这一抽象不足以涵盖整个限界上下文的业务服务。要将这些业务服务归类在一起,需要建立一个更高的抽象,例如工作或者人才,如此抽象就合理了。
02原则2:奥卡姆剃刀原则
将奥卡姆剃刀原则翻译成能理解的中文,就是“若无必要勿增实体”。这里所谓的实体,当然是指限界上下文。也就是说,在切分限界上下文时,一定要有拆分的充足理由,如果没有理由,就应该优先考虑合并,避免因为增加太多不必要的限界上下文,从而增加架构的复杂度。
例如有一位读者将直播室的业务服务单独剥离出来,定义了直播室上下文:
这样的细分有何依据呢?直播室和直播的差别又在哪里呢?如果直播室要分开,那么直播上下文的其他业务服务,如打开摄像头、关闭摄像头为何又不分开呢?
分开之前,还是先找一个能够自圆其说的理由吧!
03原则3:正交原则
遵循正交原则,可以保证限界上下文之间不存在交叉或重叠的领域知识,如此就能将变化带来的影响降到最低。
如下图的活动上下文:
该限界上下文有多个业务服务都与“报名”有关。报名不仅仅是体现了领域行为的动词,它同时还涵盖了与报名有关的领域知识,如报名通道、报名流程和报名的规则。
现在,直播上下文也有与报名有关的业务服务,如“报名参加直播”,如果二者都需要操作和报名相关的领域知识,就存在领域知识的重叠。要遵循正交原则,可以考虑单独定义一个报名上下文。说明:虽然在中文中,报名通常解释为动词,但这里的上下文名称实际上并非使用报名的动词语义,如果翻译为英文,可以理解为是名词enrollment,而非动词enroll。
04原则4:最小惊讶法则
识别出来的限界上下文,无论是限界上下文的名称,还是业务服务与限界上下文之间的关系,都要保证合理性,不能让人莫名惊讶。
例如,技术部落提供了提问、回答的功能,如果将这些业务服务归类在一起,将其命名为“问答”,就能让人一望而知其含义,如果非要将其命名为BBS或者论坛,就很容易让人产生误解了。
只要能够说明其含义,限界上下文的命名可以力求精简。如一位读者识别出了技术文章上下文和技术部落上下文,实际上,在技术部落平台这个大背景下,何必增加“技术”这个修饰词呢?更何况,技术部落上下文还很容易与技术部落平台混淆,不如直接命名为文章上下文与部落上下文。
仔细分析如下图识别出来的工具上下文,对照其名称,它包含的业务服务足够让人惊讶了。
限界上下文的名称一定要足以涵盖其内部所有业务服务的概念,做到观其名,就能大致推测出内部有哪些业务服务,而不应产生意外惊讶。上图的业务服务分配产生的不是惊喜,而是不可思议的惊诧,违背了最小惊讶法则。
以上分析了从领域维度识别限界上下文暴露的诸多问题,并一一做了解答。可以看到,我们并不满足于识别出限界上下文,还需要获得限界上下文与业务服务的映射关系,如此才能对后续的设计和建模工作产生指导作用。