领域驱动设计基础
2003年,Eric Evans完成了《Domain-Driven Design Tackling Complexity in the Heart of Software》一书,提出了一套针对业务领域建模的方法论和思想——领域驱动设计,简称DDD。DDD可以说是一种艺术性的技术,是一种复杂软件如何快速应对各种变化的解决之道。
本文的目的是通过阐述一些对DDD中常见概念的理解,同时结合一些项目的实例进行深入的分析。
领域驱动设计特点
DDD的特点主要来源于他的定位——解决业务的复杂性,而解决业务复杂性的前提是,我们不仅要有解决问题的方法,同时我们需要对问题的本身,有一种自上而下的清晰识别,而这个过程的本身,就会让初学者感到复杂。就像牛顿力学会让当时的人们,非常难以理解,但如今,我们在初中阶段都会去学习牛顿力学,并且可以很好的掌握他。因此,复杂性是相对而言的,我们不能因为复杂性就去望而生畏。
DDD解决复杂性的核心战略是积极面对,尽早发现复杂核心,划分边界,分而治之。这种方法在描述的时候,会显得简单直接,但是由于软件本身发展的复杂性,人们的关注点常常被误导,总会习惯性的将解决复杂性的重点放在了技术手段上,而忽视了针对业务领域本身去思考的视角。
努力去理解业务领域的复杂性,将软件开发的重点从技术视角转移到需要解决的问题上,发现问题、理解问题才能解决问题。问题在哪里?关键点在哪里?复杂性在哪里?这些都需要辨识。发现和理解问题以后,如何解决问题就是关键,划分领域边界、有的放矢、纲举目张是其重要方法。
复杂性无处不在,有些存在于问题之中,有些存在于问题的理解之中,有些是解决问题时带来的,针对这些问题,DDD都有独特的应对之道。
领域驱动设计难点
DDD的特点在于可以提供解决复杂问题的方法,识别问题是关键的一步,根据识别到的问题层次关系,构建问题树,从而将复杂事物像剥洋葱一样层层分解成独立的小组件。当然,这种方法存在一定的难度和额外的关注点,主要会体现在以下几个方面。
识别问题空间的不同
当我们根据业务的复杂性,分解出N多个问题的时候,这时候会带来一个新的疑问,哪些是核心问题?哪些是次核心问题?哪些不是关键问题?这些问题的层次的关系是什么?准确识别疑问这些具有复杂性,但却异常的重要,因为我们是要根据不同层次的问题去投入针对性的方案去理解和解决问题。
特斯拉创始人马斯克使用“第一原则”进行思考,这辅助他在很多的领域取得了极大的成功。“第一原则”也是古希腊哲学家亚里士多德提出的一个哲学术语:每个系统中存在一个最基本的命题,它不能被违背或删除。
如果用“第一原则” 去理解我们的业务,那我们业务中也一定存在一个业务问题,去本质的帮我们找到业务中的核心价值,也是我们发散出的所有问题的根因,是核心竞争力的体现,他更像是是一种企业战略层面的事情,属于顶层核心。而为了更好的将战略贯彻,我们需要相应的优秀战术,这也就是问题的另一个空间:产品问题,他是业务问题的细化层,起承上启下的作用。最后还存在一个问题空间,能够指导运作逻辑层进行实操,是具体执行层,也是软件具体介入的地方,是计算机语言代码运行的地方——技术问题。
因此,问题可以在本身的空间上划分出三个层面:业务问题,产品问题,技术问题。
在这三个层面上,他们自上而下的构建成一种问题定义和解决方案。业务问题为定义,产品问题也是问题定义,但是因为业务问题为顶层核心,产品问题的意义也就是业务问题的解决方案,如果业务问题不存在,那么产品问题也就毫无存在的必要了。按照这种思路,技术问题,也就是产品问题的解决方案。如果自上而下来看,谁掌握了问题的定义,谁的话语权就会更重,因为找准问题,就显得更为重要了。
统一语言与有界上下文
作为技术人员,在和产品经理尤其是萌新沟通的过程中,经常会有一种,这个产品怎么什么都不懂,我和他怎么就说不明白。产品经理也会感觉,他在说什么,为什么和交接文档上一点都不一样,什么是Map,什么是数组。
产出这种问题的根本原因是技术人员在技术团队内部的交流中,更习惯于使用技术术语,如字符串、整数、Map、List、循环等,而不是使用业务术语,而在产品团队甚至在管理层团队,大家都是使用业务术语进行交流的,那么两种团队之间就可能形成互不理解或经常误解的尴尬。
例如:在掌阅海外版打开app的时候,会首先显示一个推荐的内容,研发会称之为开屏页,但是运营更习惯称之为闪屏页。两个名词,其实都是描述的同一个事情。
当然,统一业务术语不是编制一张词汇表就可以了,术语名词还取决于不同上下文,如上面的开屏页,在不同的人嘴里就是不一样的,类似地方方言,这些都和特定上下文有关。也可能是这种复杂性导致人们的语言无法真正统一,如果发生这种不一致,就需要进行上下文中词语的翻译映射,将其明显标注出来。
所谓有界上下文(Bounded Context)实际就是有边界或有界限的上下文,上下文可以理解为环境背景。如果说统一语言表达的是事物名称,是事物内部结构特征的凝聚表达,那么有界上下文就是事物所处的外部环境背景。前面反复强调了这种认识事物的方法论:领域即边界,边界靠分类,分类须从事物内外部入手。日常生活中分析任何事物其实都遵循这样从两个角度入手的规律,提炼概念或领域模型也是从这两个角度入手才能全面。
领域模型的提炼
上节谈到了在不同上下文中语言的表达可能不同,这就为提炼模型带来了难度。在系统开发中模型可以表现为、流程图、UML符号、Java类、数据表结构等。注意这些都是模型的表现形式,模型的内容是捕捉对问题空间的基本理解,并提炼出一组特定的概念。
提炼概念或模型遵循从事物内外部入手的原则,需要明确问题空间,发现其中的复杂性,将最复杂的部分圈起来形成核心攻关领域,发现统一语言和其所处的有界上下文,根据这两者提炼出领域模型。例如,一个“交易上下文”,“交易”是这个上下文的统一术语,那么就可以提炼出一个“交易”模型。下一步是分析这个模型内部有什么、有什么结构特征、有没有产品隐藏其中,这些都是向事物内部深挖分析的步骤和方法。
提炼领域模型很难,因为对其所需的严格理解比大多数人想象的要深刻,为什么呢?因为提炼的领域模型会变成代码在计算机系统中反复执行,这样就会丧失像人工作业中针对特殊情况做灵活处理的机会。以自行车为例,如果要设计一个骑自行车的机器人,那么对骑行的理解需要比大多数自行车运动员等专家要更深刻——自行车如何能不偏斜而跌倒?自行车手会根据感觉进行平衡,但是机器人需要分析出各种力学参数。
这个骑车机器人的故事告诉我们,如果拜访领域专家的负责人,就会发现并没有现成的模型,也不能要求领域专家提供所需要的所有答案,而是要与领域专家一起分析问题来建立模型,这是一个迭代过程,需要探索许多可能的模型,并选择一个适合解决当前问题的模型。
领域驱动设计的应用场景
当前企业应用主要以SOA和微服务架构为主,这些架构主要偏重技术实现,将业务使用“服务”或“微服务”一个词语替代,服务内部是什么却被一言蔽之。服务代表业务,内部是业务逻辑,只要使用服务模型,SOA和微服务架构就能实现很多好处,实际上,业务才是编程的核心。因此,虽然使用了微服务架构,但是如果不考虑DDD,同样会遭遇和以前大泥球的单体系统一样的问题。