当Subdomain遇见Bounded Context
《实现领域驱动设计》的作者Vernon根据过去几年DDD的实战经验又写了一本《领域驱动设计精粹》,日前已经在中国翻译出版。去年底出版社找到我时,读完英文原著最终还是放弃了翻译,推荐给了其他同事,并告诉他们出版后准备接受炮火洗礼。
image不得不承认Vernon的新书在构建DDD落地体系方面较之上一本有了很大的进步,全书读起来很连贯,有一定实践基础的团队或个人均可直接上手书中很多的实践。并且通过一个案例完整叙述了从需求分析开始到最后的团队迭代开发。当然迭代运作过程中的工作量估计方式,在我看来过于简单粗暴,虽然强化了架构的最终代码落地,但却可能造成一系列的僵化。
本文主要针对Vernon一直以来对Subdomain和Bounded Context的一对一映射关系进行讨论。目标是让更多同学意识到这个方面的不同声音,从而能够加深对这两个概念存在意义的理解,并建立自己的判断。
区分问题和解决方案是个老大难问题
问题和解决方案总是像一对难以分辨的孪生兄弟,一个人看到的哥哥可能就是另一个人认为的弟弟。好像程序员在开发Story时,Story成了我们要解决的问题,具体的代码实现成了解决方案;但当BA在分析同样一个Story时,问题就成了对应的业务需求,Story只是分析出的解决方案的描述。
当然这个区分有时候可能并没有那么重要,Story到底是一个问题,还是一个解决方案,其实我们在迭代过程中并不是很关心。但有时候不做问题和解决方案的区分确是十分危险的,甚至会造成整个产品的失败。这样的例子当然是一抓一大把的,比如我经常提及的为税务审计人员提供屏幕上多记录的翻页功能,就是我职业生涯中记忆最深刻的一次失误,想当然地采用了“通用”解决方案。
Eric Evan在构建DDD的体系时显然是思考了问题和解决方案这两个维度的,我相信这个过程也是十分痛苦的,以至于最后呈现在书里的实践并没有做非常明确地划分。对于后面的实践者,包括我们自己,都存在着不一样的解读。我们曾经讨论过一个DDD实践的象限划分,但由于这样的划分太过主观,结果是一组很长的邮件讨论。
象限如下图所示,这是一个如同“PHP是世界上最好语言”般的讨论,建议大家慎入,以免上火。
image(从问题/解决方案和战略/战术 维度分析DDD元模型的元素)
这样的象限分类确实有点简单粗暴,但Subdomain和Bounded Context却是Eric明确定义的两个核心模型概念。Subdomain是对问题域的分解,而Bounded Context是对解决方案域的分解。这两个核心概念构建起了DDD处理真实世界复杂度的根基。
建模过程中很多同学其实是忽略Subdomain的,反正目标是Bounded Context。当问题相对简单时,Subdomain的划分确实给人感觉是自寻烦恼,划出Bounded Context后反过来推Subdomain视乎更容易上手。读《领域驱动设计精粹》时你会发现相似的逻辑,配合书中敏捷项目管理工具的案例(问题也挺简单)还是挺好用的。
那么为什么我们还要关注Subdomain,还要去区分什么Core Domain、Support Domain和Generic Domain呢?是否和Story一样,留给业务和BA就好,程序员还是应该抓紧搞完Bounded Context,然后开写微服务比较务实呢?
区分Subdomain的必要性
在帮助一个长期合作伙伴构建大规模DDD应用时,我写了一个“xx阶xx步”的体系。也成了很多咱们同事体系性学习DDD的开始。
一年半以后这个团队组织了所有的技术专家和主管让我又讲了一次这个体系。这次我花了一天时间让大家体会问题和解决方案的区别,加入了Subdomain的概念。参加团建时,我问了几个专家和主管他们怎么看之前的设计,得到更多是务实的“赞赏”。其实我并不在意具体落地时的裁剪,但希望白纸黑字时应该明确原委,这也是我为什么拒绝了《领域驱动设计精要》翻译的原因。
我经常用电商的案例让大家快速认识到Subdomain划分的重要性。大浪淘沙之后我们发现淘宝和京东依然是霸主。当年马爸爸嘲笑强哥构建人肉物流网的寓言也并没有发生,反而很多人爱上了京东自有物流的速度。当然站在马爸爸当年对电商问题的认知角度,自建物流是可笑的,毕竟他要解决的核心问题是如何让琳琅满目的中小供应商能够直接对接千千万万的用户,让用户能够更容易的发现适合的商品。
image所以从一开始淘宝和京东定义的Core Subdomain就是不一样的,正是问题认知的区别让两家都活了下来,并且活得很好。我们可以看到在线物品展示,吸引消费者方面淘宝一直在引领;而行业里如果你有机会接触电商领域,会发现京东物流系统还是蛮厉害的。
这是我们多年后的今天看到的结果呈现,但其实真正决定命运和格局的确是多年前两家电商对自身核心问题的理解。这个认知驱动出了两家完全不同的成功电商。
很多同学会说这玩意儿是商业模式,也轮不到我们搞研发的参与。我们拿到的都是既定问题了,再识别Subdomain也没啥意义了。这个论断有两方面问题:
- 作为产品和服务的实现者,如果都不参与和关注问题本身的划分及核心子问题的认知,那么你很可能在浪费自己的时间,开发出未来被边缘化,甚至淘汰的系统。这不是危言耸听,在我的最近咨询过程中已经鉴证了很多次,比如在这个移动优先的时代去强化PC应用的技术架构。
- 其次在这个软件应用空前发展的时代,始终抱着所有模块都必须是“自研”,所有代码都必须自己写的思想,毫无疑问只能成为“小作坊”。构建现代的复杂系统已经逐步成为一个生态工程,随着数字化服务的普及,识别哪些领域应该直接外购使用也成为了开发团队的重要能力,构建一个典型的移动应用应该没有人再会去重头写一个二维码扫描模块,而是学会从市场上选择适合的软件包。
那么什么地方应该建,什么地方应该买,应该如何决定呢?这时候我们会发现Subdomain的划分就非常有指导意义了。类似二维码扫描这样的Generic领域显然应该是外购的,而当年京东对电商的理解来看物流系统是要自建的。同样道理还有上次DDD China大会来分享的盒马生鲜,半年时间已经重写了三次核心ERP系统。不去思考问题划分的同学们会觉得盒马疯了,ERP在外部看来是多么成熟的软件包啊~ 但事实上盒马生鲜的本质就在如何解决生鲜食品的高效配送上,也可以说是一家特殊的物流公司。
image小结一下,即使区分问题和解决方案很抽象,划分子问题很烧脑,我们还是必须认识到分析问题本身的重要性和必要性。借用雷布斯的成名句“不要用战术上的勤奋掩盖战略上的懒惰”!
Subdomain和Bounded Context的对应关系?
探讨了Subdomain的必要性,自然我们需要分析和解决方案这边Bounded Context分解的关系。第一次看Eric构建的DDD模型脑图(如下)时,我一直认为少画了Subdomain和Bounded Context的对应关系。最早采用DDD时,个人认知是一个Subdomain下应该有多个Bounded Context,即当我们分析出了一个子问题后在针对建模的解决方案进行分解,成为多个Bounded Context。所以Subdomain:Bounded Context应该是1:N的关系。
image(Eric构建的DDD模型脑图)
然而Vernon一直以来的实践方式隐含着1:1的对应关系。这样的对应关系并非没有道理,如果咱们从一个Bounded Context出发,我们会发现每个Bounded Context必然应该是“解决”部分问题的,而这个部分问题是否就应该是一个Subdomain呢?
当我们拿着这个差异去跟Event Storming的发明者Alberto Brandolini讨论时,发现对方委婉地表达了N:N的理解。简而言之没有直接的对应关系。当然这种理解隐含了一个Bounded Context是可以服务于多个Subdomain子问题的。比如“产品展示”Bounded Context的模型可能服务于产品销售和产品评论两个Subdomain子问题。
这三个对应关系的理解暴露出了大家对问题和解决方案这个老大难问题的纠结~ 当然最简单的是能够建立一对一的映射,作为解决方案高手的程序员们显然是非常喜欢这个模式的。以至于很多用DDD建模的程序员直接就跳过Subdomain搞起了Bounded Context。当然这也是我坚决反对这样简单化映射关系的重要原因。
出于对方法实操性的考虑,我仍然认为一对多的映射是最优的选择。诚然在我们的现实世界里,问题和解决方案是没有必然对应关系的,他山之石可以攻玉也是古来有之的。但软件设计本身就是一个问题抽象的过程,这个抽象一定会选取一个视角,也就会放弃部分信息。在这样的认知下,其实我并不介意在不同子问题的解决方案里存在一定的重复。
所以,如果让我来站队Subdomain和Bounded Context的对应关系,我仍然会选择一对多。在准确性和易用性之间寻求一个平衡,并保证大家能够更多的关注问题本身。
坚持持续认知问题
Subdomain和Bounded Context的讨论随着DDD实践的深入会进一步被大家所讨论,不论大家是否能够共识,这样的讨论都是有好处的。作为软件开发的从业者,在面对这个越来越多不确定性的数字化时代,认知问题本身将越来越重要。
Subdomain和Bounded Context在实际认知过程中一定也是相辅相成,逐步清晰的两个概念。Bounded Context建立一定是针对Subdomain的;而Subdomain的划分又会通过Bounded Context的模型得到持续地验证。
文/ThoughtWorks 肖然