领域驱动设计(domain driven design)战略篇之二 Bounded Context

之前的一篇文章谈了战略ddd的重要性与Bounded Context这个概念,最近在油管上看到一个2017年关于domain driven design的演讲。如下
领域驱动设计(domain driven design)战略篇之二 Bounded Context
感觉与自己现在讲的主题十分相关,正好在这里展开说一下。
他认为Bounded Context可能是ddd中最重要的概念。而悲剧地是ddd的社群可能更倾向于讨论战术方面的各种设计模式,他顺便也吐槽了一下ddd这本书的章节编排。
我也很赞同他的看法。具体说一下这两点。

1. ddd的战略部分容易被忽略

还是摆出这张图
领域驱动设计(domain driven design)战略篇之二 Bounded Context
学习ddd的人在开始可能更热衷于战术层面设计模式。如上图里repository, specification, aggregate那部分概念,以及在代码层面如何实现它们。
本人自己也是如此,在读《domain driven design》时基本把战略部分给跳过了。而且战略部分也是书的最后几章。读完了战术篇的部分,就已经觉得
领域驱动设计(domain driven design)战略篇之二 Bounded Context
也由于此书对读者的极其不友好,读到最后几章时基本气力已经耗尽。(如下图,全书共17章,Bounded Context居然到了14章才登场。)
领域驱动设计(domain driven design)战略篇之二 Bounded Context
然而,在实际的项目中,如果我们只是去讨论如何实现这些设计模式,那会是很可笑的。最重要的是如何实现项目的需求。虽说如此,本人在写博客时,还是先从ddd的战术开始写的。原因大概有这么几点
1.1 这反映了自己对ddd的认知历程。本人一开始也深陷ddd战术的话题,而对战略层面的东西基本选择跳过。所以认为很多人学习时也会遇到同样的问题,如果一开始就谈战略,可能基本都会被大脑过滤掉。
1.2 由于1.1的缘故,战略方面的知识也是自己薄弱的部分。
1.3 ddd战术的设计模式比较具体,容易在实际项目上使用。而想战略层面的Bounded Context的话,很抽象,这就造成这个说明很困难。(尤其在自己的理解不是很透彻,很多情况都是人云亦云的情况下)
从结论上说,自己还是做了妥协,把比较重要的部分挪到了后边来讲。

2. Bounded Context是很重要!

Bonded Context是一个帮助我们拆分系统很好的概念。它促使我们去寻找边界,定义边界。这里先说说Jimmy Bogard在视频中的例子。Jimmy Bogard他曾经为美国德州*建造一个管理青少年犯罪事物的系统。
他最后整理出的一张表示这个系统的图。
领域驱动设计(domain driven design)战略篇之二 Bounded Context
当然这只是想表达这个系统的复杂度。
他还整理了使用这个管理系统的用户,大概有七十多种。
领域驱动设计(domain driven design)战略篇之二 Bounded Context
接着头痛的事情来了。为了设计一个庞大的系统,需要与各个部门去沟通,各个部门又有不同的概念和用语,而让各个部门的人对某一个概念拥有共同的认识也基本是不可能的。比如对青少年的解释,各个部门就是各有各的说法。
领域驱动设计(domain driven design)战略篇之二 Bounded Context
执法部门(图中的LEA)根本就不管什么青少年。他们觉得这只是某张表格。。。法院(图中的Courts)觉得青少年就是判决对象。检察机关(图中的Prosecution)认为青少年就是起诉的对象。面对这种境地,他们尝试了创建非常抽象的概念,如人Person,但实际上在实际的业务中,没有使用person这个概念。这就悲剧地表示建立的模型没有对应的业务概念。这当然会有悖于model driven design这种思想。
此外各个部门关注的事情也不同。比如执法机关只关注”抓人”。法院则关注“审判“,检察机关则直观“案子“(可能指的是起诉)。
领域驱动设计(domain driven design)战略篇之二 Bounded Context
如此多的人物登场,而所关心的业务也各不相同,其实是一种征兆。尤其出现如“青少年“这种貌似是同一个概念却有着不同解释及业务的情况时,我们就应该意识到我们在不同的Context里讨论某个概念,划分界线,使用不同的模型可能是一个更好的选择。
当时Jimmy Bogard在承包这个项目时也觉得,他在做一个系统想要满足所有人,所有需求,这实在是难度太高,根本不切实际。不过他还是义无反顾地奋勇向前了。原因是甲方只想做一个系统。而当时也没有SOA, microservice什么的,他可能也没有拆分系统的好的手段。
最后项目时上线了。但这份经历基本算是用来反省的。这个项目最终也成了big ball of mud,没有人愿意触碰的系统。据说被开发人员称作一拖翔。

不关注Bounded Context的潜在危险

从上边的例子,应该不难想象,当系统过于庞大时不拆分Bounded Context的痛苦。再结合一下《domain driven design》一书说说无视Bounded Context的危险。

对模型的错误认识

在《domain driven design》一书中就有一个例子。说是某公司要开发一个customer-invoicing(发票)模块,需要一个叫Charge(收费)的对象。expense code(支出代码)与posted amount(记账金额)两个属性。开发团队A发现另一个开发团队B的代码已经存在了Charge这个对象。不过这个Charge没有expense code和posted amount属性。于是团队A对加上了expense code,又发现Charge有一个叫amount due(未支付金额)的属性,他们以为和posted amount应该是一个意思,于是就把这个amount due改成了posted amount。
领域驱动设计(domain driven design)战略篇之二 Bounded Context
团队A之后又为Charge加上了几个方法,在不影响既有功能的情况下作出了customer-invoicing模块。同时因为Charge对象是借用既有的东西,Charge对象里包含了和customer-invoicing完全没有关系的各种逻辑。当然这个模块发布后各种报错。很明显本来就不该用同一个Charge。(当然这个故事听上去是有点扯~)
如果再用Bounded Context划界的情况下,开发团队A可能就会意识到,在他们的这个Bounded Context里还不存在Charge这个对象。而开发团队B使用的Charge是在他们的Bounded Context中才有意义的模型,团队B设计Charge这个模型的目的和用途可能是不一样的,所以不能拿来就用。
从自己的开发经验上讲也是如此。对没有划界线的东西自然会倾向于重用。这很容易导致我们用现有的模型(model)套用到新的问题(domain),而忽略了是否应该使用完全不同的模型。最终这也会导致我们的系统变得过于复杂,难以维护。更可怕的是我们可能制造出与需求完全不同的东西。

维护统一模型的沟通与理解的成本

如果一个系统由多个团队同时开发,而没有明确的界线。团队A修改的东西要注意不影响团队B,如果有影响还要与团队B去协调。然而当项目规模到达人脑难以完全把握的程度,即使有团队沟通后也不一定能百分百地解决问题。
按自己的工作经验来说讲,团队按照不同的业务应对分了组,理论上在业务层面有各自的界线,但开发时并没有在意这种界线,反而去一些建立一些横跨多个服务的共用模型(如下图的common models),让这个模型应对不同服务的需求。
领域驱动设计(domain driven design)战略篇之二 Bounded Context
结果界线其实就是名存实亡。结果就是明明团队A要开发一个功能,但这个功能会影响到团队B的东西,接着就要想团队B去确认影响,如果团队B的人记忆力不好还得全部自己查。
当然这样的设计有一定的历史原因。如项目本身只有一个database。project A, project B, project C的数据其实是共用的。然后用orm生成了所有table的java entity类。自然习惯性地都把他们放到Common Models中,最终也变成了big ball of mud。

ddd对Bounded Context的定义

Bounded Context是一个抽象的概念,而且在各种设计思想中被借鉴。说了一大堆它的重要性,最后希望能给这个抽象概念有更好的解释。
很遗憾在《domain driven design》一书中虽然出现了Bounded Context,但感觉没给出很明确的定义。而关于Context则有这样的说明。

The context of a model used in an example in this book is that particular example section and any later discussion of it. The model context is whatever set of conditions must apply in order to be able to say that the terms in a model have a specific meaning.

Context是能够明确一个模型意义所需要的所有条件集。Bonded则突出了“界线“这个意思。Bounded Context是有界线的Context,在这个Context中的模型,意义是明确的,没有歧义的。
从自己的项目经验上来讲,自己还是比较实用主义。在系统划分时就是使用了Bounded Context这个用语,而没有十分在意它的实体是什么东西(可能就没有实体)。不过至少Bounded Context这个概念给出了一个启示。不要去设计一个能够应对所有情况的模型。把模型要解决的东西限定在某一个范围。而这个范围就是一个Bounded Context。
另外这几年微服务也十分流行,在《Building Microservices》一书中,作者也谈到Bounded Context。个人也觉得microservice其实于Bounded Context有着映射关系。
领域驱动设计(domain driven design)战略篇之二 Bounded Context

总结

这次通过实际的例子又讲了讲Bonded Context这个概念。帮助我们更有目的性地去建立明确的模型。那当我为某个项目拆分出各个Bounded Context后,接下来又能做什么呢?下一篇文章讲讲Context Map