1.3 DDD的价值分析
现在我们知道了可以以最低复杂度——业务复杂度来开发软件。我们还可以单独构建系统最难的部分——领域逻辑,这是DDD的生命力所在。深谙架构原理和工作经验丰富的架构师都知道这两句话的重要性。但是,我们可能需要更具体的指标,以便让老板和团队看到它的价值。
本节将从三个方面继续分析DDD的价值,它们也是软件工程的三个基本要素,分别是复杂度控制、架构原则和团队协作。如图1-7所示,该图像一个三棱镜,将DDD最本质的光谱图折射出来。我们从这三个基本维度分析,看看DDD会有怎样的表现。
图1-7 DDD透视三棱镜
1.3.1 复杂度控制
我们经常谈论复杂度,但似乎没有向读者解释为什么在谈到软件工程时总是考虑复杂度,以及在哪些方面会出现哪种类型的复杂度。本小节将详细讨论这个话题。
复杂度是软件的本质属性,最早来自软件工程的权威著作之一《人月神话》中的一篇文章“没有银弹”。以下是书中的观点摘录:
❑ 软件的复杂度是根本属性,不是次要因素。
❑ 从规模上来看,软件可能比任何由人类创造的其他实体更复杂。
❑ 复杂度会引起大量学习和理解负担,让开发过程逐渐变成一场灾难。
总的来说,软件是最复杂的人类制品。这可能是因为软件是一个“纯逻辑”的浑身上下遍布着各种开关的“生物”。心理学研究表明,人类的脑力资源是有限的,无法应付过多的关注点。比如,心理学家做过研究,一般人对七层以上括号的程序是无法理解的。人脑潜意识里也会逃避过度耗费脑力的活动。
因此,相对传统行业,软件开发对脑力有更高的要求,也给设计方法论、团队协作带来了前所未有的挑战。开发的管理至今都是一个世纪难题,你只需要看到那种“走一步看一步”的敏捷方法如今是多么流行,就大概明白是怎么回事了。
任何方法论一定要有控制复杂度的方法,不然是没有生命力的。但似乎《人月神话》的作者Brooks认为当时所有的方法都不可能根本性解决复杂度问题,所以书中最后的结论是“没有银弹”。那么,DDD是不是银弹呢?我认为是完全可以胜任的,如果你理解了它的底层逻辑,遵守了它的原则;但也很可能不是,如果你只是应用它的战术模式。下面我们看看DDD的原则是如何解决复杂度这个问题的。
(1)穿透熵增之墙
如前所述,DDD的第一个原则——语言、模型、代码三者一致,保证了我们以最低的复杂度来构建软件,在业务之上添加任何一层设计都会增加复杂度。那么,复杂度是如何产生的呢?我们将这个过程称为穿越熵增之墙,如图1-8所示。
图1-8 熵增之墙与反馈之路
在传统项目中,领域专家与开发团队的业务沟通会被翻译成需求说明书。领域专家使用他们自己的术语,而技术团队所使用的语言则经过调整,目的是从设计角度讨论领域。需求说明书中的术语与沟通中使用的术语不一致,导致的结果是对领域的深刻表达没有记录下来,或者即使记录下来了,我们也不得不请作者给我们翻译。领域专家也无法直接验证对方的理解是否一致,我们称之为理解之迷雾,这产生了第一层熵增。
由需求说明书到架构设计,又是一次割裂的痛苦。此时,需求说明书仅仅是架构师用来理解需求的工具。架构师往往认为把业务架构与技术架构联系在一起会破坏技术的纯粹性,所以,技术架构与需求说明书中的业务架构的联系非常松散。这产生了第二层熵增。因为这一次跨越了业务和技术,我们称之为跨界之迷雾。
第三层熵增来自我们个性又有活力的程序员,基本上代码工作一开始,需求说明书和设计架构就被抛到了一边。他们会重新设计,重新学习业务知识。然后基于自己的理解,完成最后的成品。如此自由的做法,我们称之为个性之迷雾。
领域专家看到成果,团队得到反馈,是在经历了上述重重迷雾之后,再经过环境和数据准备的艰难险阻(可用DevOps克服),才能在浏览器中验证业务逻辑。当然,结果很可能是不理想的,团队在哪一个环节脱节了,我们就不得不再穿越一次迷雾之海。中间任一环节的产出物,都不能用来验证领域逻辑,代码也揭示不了业务目的,导致的结果就是不仅验证领域逻辑困难,灵活性也很差。
而在DDD中,我们有通用语言及三者一致的要求,那么图1-8就变成了图1-9,我们也可以参考图1-3的单一模型的效果。
图1-9 通用语言之桥
除了DDD,为了穿越迷雾之海,业界的很多方法论也在做着各种努力,可以达到一定的效果。比如,敏捷用户故事就是其中一种。我们注意到,开发人员领取的是用户故事,而不是需求点或功能模块。到开发完成,用户故事也成为测试和验收节点,这无疑解决了一些内部熵增导致的目标偏差。
但这些方法论因为没有明确地看到背后的迷雾之海,并直指要害,相对于DDD而言,依然有反馈路径过长的不足,一般要等功能开发完毕,环境部署就绪后,领域专家和团队才能看到工作成果,并给出反馈。同时敏捷还有个问题,用户故事抓住了两端,但设计不是那么透明,这一点也不如DDD。DDD和敏捷过程将在第2章继续讨论。
(2)分离两种复杂度
前面我们讲到,复杂度包括两种:业务复杂度和技术复杂度。业务复杂度是指领域固有的计算逻辑、规则约束、判断流程等,不依赖于系统而存在,还包括领域专家与开发团队沟通中会产生的各种不确定性。技术复杂度包括与实现相关的所有内容,比如语言框架、残留系统、技术组件、技术环境等,还包括与领域逻辑不相关的关注点,如交互界面、安全机制等。
这两种复杂度单独来对付并不可怕。毕竟开发人员这个群体在抽象和逻辑思维方面不会比其他行业人员差,一个初级程序员学习一个技术框架也没有那么困难。最可怕的是两种复杂度交织在一起的情形,两者之间没有清晰的界面(见图1-2),无法确定哪种业务逻辑散布在散乱组织的代码的何处,这被称为大泥球架构。
两类不同的关注点混合起来产生的各种排列组合,往往不是人的脑力所能胜任的,而且也不符合人类关注点分离的思考模式,软件变得难以构建和管理。此时只能通过不断地切割系统,把复杂度限制在一定范围内,就如同潜艇的密封舱一样。
当然很多读者会说,我们的项目有良好的层级和模块划分,不是大泥球。但如果我们的设计和实现与领域理解没有任何联系,都是自己一套独特的设计,或者我们没有把保持领域模型的内聚性与独立性作为原则的话,这种良好的感觉将随着项目规模的增加很快消失。
我们看一下领域模型的内聚性与独立性是如何解耦业务与技术复杂度的,如图1-10所示。
图1-10 领域模型隔离了两种复杂度
从图1-10中可以看出,领域模型作为沟通的核心,代表了领域专家与技术团队的共识,是他们沟通的媒介,隔离了业务复杂度和技术复杂度。
所有业务复杂度在领域模型层面都得到了收敛,技术复杂度从领域模型处都找到了根源。作为两者的清晰界面,我们只需在领域模型的绿洲内畅饮,而无须跨越技术复杂度与业务复杂度的熵之沙漠。团队完全可以先把重心放在领域模型的构建之上,后面再围绕核心领域层搭设系统的技术架构的其他部分,组织相关的开发任务(详见第10章)。
内聚性保证了相关的业务逻辑都在一起,这个特性使领域专家和开发团队共同打造好领域模型之后,守住这个鸡蛋篮子就可以了。领域专家可以检查业务逻辑的覆盖程度,直接编写验收测试用例,不用担心领域逻辑被遗漏。任何对业务理解有歧义的地方,在领域模型的构建过程中都会得到统一。
独立性提供了领域模型的稳定性,技术性变更不会影响到领域模型,可以被很方便地测试,任何人在业务逻辑发生变化时,都可以愉快地启动单元测试套件来测试业务逻辑。在开发人员的指导下,业务人员是可以看懂实现与测试代码的,可以方便地开展静态测试、代码走查。在不同的应用中重用领域逻辑,也有了可能性。
领域模型这个绿洲里蕴含丰富的生态,它阻止了两种复杂度的叠加影响,让各方都能在其上放心且高效地工作。而在两种复杂度交织不清的沙漠里,生物是很少见的。
1.3.2 架构原则
从架构原则分析DDD的价值,是因为架构的基本原则体现了事物的本质规律。良好架构的特征,如高质量、可靠性、灵活性、易维护性等,都是遵循这些原则的结果。
实际上,我们之前说的许多道理,往往可以用“遵循了×××原则”来概括。对于深谙架构原则背后原理的架构师来说,这更有说服力。同样,一个设计方法如果不能保证这些架构原则,绝对不是一个好迹象。
架构的基本原则不局限于评判DDD的合理性,而适用于所有的技术决策。作为决策的依据和取舍的标准,后续会采用各种设计模式来构建智慧的领域模型(详见第9章),到时还会大量应用这些原则来说明问题。
我们先对这些原则逐一阐释,以达成对这些原则理解上的一致。
1.软件架构的6个原则
原则1:语义一致性(Semantic Coherence)原则
直观理解就是相同的表达,含义要一样。放在软件架构中理解就是相同的逻辑,代码中只能有一个处理的地方。良好的架构不会为做同一件事情提供两种方法。
在所有的架构书中,都少不了这一个原则。它还有一些其他的名字,如“用相同的方法做相同的事”“一处一个事实”和“不要重复自己”。这个原则初看可能平平无奇,刚入门的程序员也会把共有的代码提取到一个函数中去,但有经验的开发人员都知道,伴随着系统规模与团队的增长,交付软件中的种种怪异行为都可以归结为对这一基本原则的违反。
原则2:开闭(Open-Closed)原则
软件模块对于需求应该是可以扩展的,但对于代码修改是关闭的。
这一点乍看起来有些矛盾,如何能在不改变代码的情况下而满足新的需求?但这恰恰是设计要去达成的目标,或者说解决的问题,并且技术上是完全可以实现的。
之所以有这个原则,是因为代码改动的代价太大了。问题还不仅仅在于变更以后的冗长的发布过程会影响系统的可用性,而是不知道更改会对系统产生什么诡异的级联效应。而不改动或者少改动就等于稳定,稳定就意味着更少的缺陷和更好的维护性。
我们可以采用预测变化点的方式,使架构符合开闭原则。对需求变化的预判能力和对业务逻辑的深入理解,是我们构建出符合开闭原则要求的架构必不可少的前提。技术上则可以采用接口、事件、晚绑定、多态、参数化和配置文件等各类形式。“把系统中稳定的部分与变化的部分分开管理”这一说法也是开闭原则的延伸。
原则3:最简(Minimize)原则
最简原则也常被叫作KISS(Keep It Simple and Stupid)和YAGNI(You Ain’t Gonna Need It)原则。用最简单的机制来满足需求,不要引入不必要的组件、框架等,用极简的方式添加功能,会得到更健壮的系统。
这个原则经常被架构师群体所提及,这其实是一个指导原则,背后的原因依然是我们前面提到的复杂度控制原理,用来应对系统的熵增定律。除非必要,不要引入不必要的复杂度。
原则4:高内聚低耦合(High Cohesion & Loose Coupling)原则
内聚指的是模块内部的关系。高内聚即关系紧密的逻辑整体要组织到一起,每个模块都要有清晰定义的角色,不相关的逻辑不要放在一起。
耦合说的是模块之间的关系。低耦合是说模块之间的依赖关系要尽可能少,依赖类型要弱,甚至完全不知道彼此最好。
为什么高内聚是架构的一个良好的特征?一方面,它符合人们认识客观事物的规律,相互关联的东西都是在一起,且有自己的边界,就像细胞被细胞壁包裹一样。另一方面,人的注意力是有限的,组织和提炼知识可以有效地减少关注点,而散布的逻辑显然会让这一方法失效。
一个系统的各模块之间完全没有任何耦合,似乎是不现实的,因为它们还是要在一起工作来完成任务。我们要做的是降低耦合的影响,在一个要素改变后,不要影响到其他要素。依赖的关系也要遵循,外围的组件依赖核心的组件,脆弱的部分要依赖稳定的部分。最核心、最稳定的部分是不需要知道其他部分的存在的,比如领域层。
模块之间的依赖类型,除了我们熟知的直接调用这种依赖类型外,还有多种耦合方式,它们都比调用关系的耦合更严重,破坏力更强。比如,模块对特定环境条件的依赖,在某个位置必须存在某个资源,否则系统就会失败。或者模块对外部模块特定实现方式的依赖,比如消息体必须按照某种格式组织,必须按照某种顺序传递,否则就无法正常工作等。这些依赖类型是绝对要避免的。
原则5:关注点分离(Separation of Concern)原则
关注点是指事物变化的两个独立的维度,它们原则上是不受彼此变化影响的,比如一个点的横坐标与它的纵坐标。关注点分离的意思就是,彼此不相关的关注点应该在架构组织上将它们分离,而不应该放在一起彼此影响。如浏览器中的HTML负责内容,CSS负责样式,JavaScript负责交互,就是一个典型的例子。
原则6:可构建、可测试(Buildable & Testable)原则
良好的架构应该是容易被理解的,支持增量式构建并且方便测试。
这是从工程角度提出的原则。撇开维护良好的团队关系不谈(开发与测试),架构在这方面的努力是最值得的。一个架构方法在今天不能支持单元测试、敏捷或DevOps的快速迭代测试,显然是落后于时代的。
以上就是我们列出的主要架构原则,当然读者也会想到自己熟悉的一些设计原则,但大部分原则都可归为上述原则的等效原则或延伸。如单一职责与高内聚原则其实是一样的,其目的都是满足良好的开闭原则。
那么当我们套用这些原则观察DDD时,表现究竟如何呢?
2.DDD对架构原则的体现
(1)语义一致性原则的体现
对于DDD的第一个原则,其中的语言就是要消除各方对业务理解、表达与词汇上的各种歧义。同时,语言是以模型为支柱的,其目的也在于消除沟通和设计环节可能产生的理解偏差。开发团队与领域专家都需要重新通过模型来理解领域逻辑,模型代表了唯一的事实。这一点完全符合语义一致性的要求。
同样,所有的业务逻辑都必须封装在领域模型中,领域模型是调用业务逻辑的唯一入口,是语义一致性原则的充分体现,保证了“一处逻辑只有一处代码”的原则。
(2)开闭原则的体现
就业务逻辑来讲,要想在不改变模型代码的条件下扩展需求,最好是应用一些设计模式(见第9章)。DDD一般采用面向对象语言建模,在构造块构建的战术层面,运用了很多设计模式,如工厂方法等。对的需求场景搭配对的设计模式,可以保证在需求发生变化时,领域模型无须做任何改动即可满足需求。
在架构和上下文集成的战略层面,DDD亦有多种解决方案,如六边形架构、共享内核等,这些模式最大限度地保证了在技术环境变化时,领域模型不受外部因素的影响。
构建有生命力的领域模型,就是让我们的领域模型面对需求变化时,符合开闭原则。智慧的领域模型是对业务的深入了解、对模式的应用场景的掌握以及和创造性思维相结合的成果。
值得注意的是,如果应用模式解决了问题,团队就要努力让模式的名称成为通用语言的一部分,让领域专家去理解,而不是把它们隔离在设计之外。
(3)最简原则的体现
如果你了解奥卡姆剃刀原理——如非必要勿增实体,那么就可以看出DDD是符合最简原则的。
DDD的特点就是开发人员不会先入为主地预先设计领域模型,而是从与领域专家沟通的语言中导出设计、提取模型。完成的模型会与领域专家沟通,获得反馈,以检查是否符合领域的工作方式。以最贴近业务本身的工作方式来构建模型和组织代码,最后还要得到领域专家的确认。这些实践都符合最简原则。
三合一原则意味着中间不存在二次设计和转译,这是一种返璞归真的工作方式,是最简原则的完美体现。
(4)高内聚低耦合原则的体现
❑ 高内聚:相关的业务逻辑必须组织在对应的领域模型中,而不能在别处。为了体现内聚性,DDD还有“聚合”的概念(见第6章)——把相关的领域实体组织在一起。高内聚的领域模型是DDD的基本原则。
❑ 低耦合:领域模型完全独立于技术架构的其他部分是DDD的基本原则。适用于DDD的六边形架构保证了领域模型的独立性。可以说,领域层与技术架构的其他组件之间,耦合程度是最低的。还有一些模式的运用,如工厂、资源库模式,也把其他层与领域层的耦合降到最低。上下文之间的集成(见第7章)也有多种可用的解决方案。
(5)关注点分离原则的体现
通过单独构建领域模型,DDD分离了业务复杂度与技术复杂度,这是最大的关注点分离。DDD中的子域分别有核心域、支撑域和通用域。上下文画出了模型和语言的边界,进而划分了用例、工程和团队,都是关注点分离原则的体现。
(6)可构建、可测试原则的体现
DDD的易构建性显而易见。在使用通用语言沟通中,领域模型的构建是很自然发生的,不需要“技术翻译”。而领域模型作为业务复杂度和技术复杂度的隔离带,使软件的易构建性、可维护性大幅提升。
DDD可测试性的优势体现在测试和验收业务逻辑时,只针对领域模型进行单元测试即可(详见第8章)。
单元测试和静态测试是效率最高的测试类型,但很多团队无法开展,其中的原因是缺少可供测试业务逻辑的“单元”。在没有使用DDD时,业务逻辑与程序模块之间并不是明显对应的,两者往往是错位失配的。这种情况下,即便我们想测试,也因为这些单元缺少内聚性和独立性及测试对逻辑的覆盖度不够无法独立开展测试。静态测试代码也因无法看出代码和需求的对应关系,难以开展。
而在DDD中,领域模型可以成为单元测试的完美抓手。领域模型越强大,单元测试对需求的覆盖就越全面。代码作为模型的表达,也完全可以通过静态测试发现其中丢失的逻辑和问题。
DDD对于架构原则的体现如表1-2所示,这充分阐释了DDD在架构方面的价值。
表1-2 架构原则与DDD
1.3.3 团队协作
下面从团队协作角度评估一下DDD带来的价值。
在一个软件开发项目中,除技术外,团队或人的因素有多重要?这里不直接回答这个问题,而是列出软件工程上两个有名的关于人的“定律”,请读者自行体会。
❑ 定律一(Brooks法则):向进度落后的项目增添人手,只会使项目更加落后。(《人月神话》)
❑ 定律二(帕金森定律):无论你给项目团队多少时间,团队都能将其耗完。(《人件》)
虽然上面两本书都已经过去几十年了,但没有哪个团队管理者会否认上面两个定律的强大生命力。如此“奇葩”的定律,一方面是因为我们前面提到的软件的复杂度。就像人脑的神经元连接密度远远大于人的躯干一样,软件开发的高复杂度客观上需要团队更多的协作和沟通,沟通中的信息密度也远远超过了其他类型的团队。如果团队意识不到构建良好的协作机制、通畅的沟通渠道的重要性,对项目的不利影响将是致命的。总的来说,有以下几对重要的协作关系,我们必须重视。
1.团队协作的4种重要关系
关系1:领域专家与用户之间
面临的挑战:领域专家和用户通常不来自同一团队,这种情况很少出现。领域专家可能并不总是同意用户的某些概念和术语,因为他们有丰富的工作经验或深厚的行业咨询背景。用户之间也不总是一致,公司的不同部门隔开了不同的“上下文”(详见第7章),他们的工作方式和对软件的要求可能是完全不同的。
此外,我们必须意识到,客户的业务逻辑本身也在不断进化。只有沟通才能消除业务人员之间的分歧,甚至推动一些重要的商业决策。
关系2:领域专家与开发团队之间
面临的挑战:这是项目团队中最重要的一对协作关系。但是,如前所述,如果业务架构与技术架构之间错位失配,团队的精力将会大量消耗在两者之间的转译上。
领域专家和用户基本上会被技术团队的一句“你不了解实现方式,这个需求实现不了”而挡在门外,技术团队也会因为一句“这个需求你和谁确认了”而感到沮丧。更不用说,在很多项目中,程序员实际上不是在编写代码,而是在自己摸索业务领域知识,其后果不言而喻。
关系3:开发团队内部与开发团队之间
面临的挑战:开发团队内部首先是设计师与程序员的基本矛盾。
❑ 设计师所担心的:程序员是否理解设计并完全遵从设计来开发。
❑ 程序员所担心的:架构师的设计根本无助于实现。设计与需求不一致或者已经过时,需要自己重新设计。
开发人员之间还面临着以下问题:
❑ 能在什么粒度上重用别人开发的代码。
❑ 有模块依赖的开发人员之间该如何并行工作。
不要小看这些问题,以第一个问题为例,单纯地强调“无重复代码”是容易的,但由于封装和重用级别需求的不清晰,缺少内聚的模型,随意重用别人代码会产生不可预知的副作用,导致缺陷不断地复现。这种情况的发生会让缺乏耐心的开发人员放弃重用的想法而另起炉灶,违背了基本的原则。
内部沟通还涉及团队成员与新加入者之间的沟通。在这种情况下,有人会给新加入者讲解需求,有人会为其提供技术架构培训,但是,也可能没人知道或者很模糊。即便有相应文档,往往也已经过时。学习者面临着一个陡峭的学习曲线,这个混沌的领域就这样被搁置了。当他们着手开发代码时,碰壁和挫败是难免的。因为需求、设计和代码之间完全是失配的关系,跨越它们之间的多个多对多关系导致系统的复杂度剧增,很难应对。
开发团队之间则面临如何在清晰的界面上集成的问题,这通常是由于缺乏约定的模型和良好的接口而导致的。稍有不慎,就可能会导致像下水道堵塞这样的问题。
关系4:开发与测试之间
面临的挑战:前面提到,单元测试是最有效率的测试,而静态测试也是非常有效率的测试。它们定位准确,修复缺陷的代价极低,这一点我们会在第8章中详细讨论。
但这“唯二”的高效测试在多数团队中都不能顺利开展,原因很简单:
❑ 代码根本看不懂,测试人员无法开展静态测试。需求说明书与测试代码之间就如同Java与JavaScript的关系一样。
❑ 单元测试无法覆盖业务逻辑,因为它们并不“住”在一个单元里。
❑ 单元测试无法单独运行。
结果就是测试人员只能求助于更高级别的端到端的测试来验证功能,而这种测试是最难实施和定位缺陷的。即便是稍好一些的接口测试,测试人员也很难分辨测试的究竟是业务还是设计。
2.DDD对于团队协作的促进
下面我们看一看DDD是如何改进协作关系的。
(1)领域专家与用户之间
DDD强调领域专家的参与,以及统一语言的建立。随着统一语言的建立,领域专家和用户之间存在的概念分歧会逐渐消失。当用户体验按照领域专家中的模型设计时,软件对于用户来说就变得容易理解了。甚至不需要对用户进行培训,软件本身就可以起到培训的作用。随着统一语言成为工作语言,业务人员能更好地理解业务。
(2)领域专家与开发团队之间
领域模型都是基于通用语言构建的,同时领域模型是通用语言的核心。代码即模型,代码即业务,三者之间不存在失配,所以也就没有业务架构向技术架构转译这种说法。由于领域业务模型的显式存在,领域专家和技术团队之间对业务规则的理解是一致的,设计和实现对业务规则的覆盖是否完整也是清晰可见的。
因此,需求实现与变更的可能性对于领域专家和用户来说也是透明的,开发人员再也没办法说出那些晦涩的理由。不要小看这个特性,它可以解决80%的团队争吵,带来最想要的客户满意度。
(3)开发团队内部与开发团队之间
首先,三合一原则保证了代码对设计模型的完全遵从。所有的开发人员(至少是领域层的)都是模型的共同创造者,必须学会用代码来表达领域模型。没有过度的自由,就没有犯错的空间。
DDD对业务实体的封装、接口的命名、聚合的限制以及领域服务与应用服务的区隔,都使得我们在重用的问题上有清晰的判断,不会犯严重的错误(见第8章)。
并行开发的问题通常也可以通过关键组件的模型化来解决,模型并不一定满足所有需求。但只要接口由通用语言所定义,它就不会轻易地变来变去,开发工作就可以顺利地并行开展。
在新人培训方面,一个有生命力的领域模型的生存周期可能比项目团队要长得多,模型和代码都是最好的学习资料,并且它们是一致的。后续加入的开发、测试人员学习业务和掌握代码是同步的,因为它们就是一回事。在DDD团队中,新加入的产品经理通过代码(尤其是测试代码)来了解旧的需求,也无须太过惊讶。
开发团队之间的集成工作稍微复杂一些,庆幸的是,这是DDD战略层面主要关注的内容。很多权威的著作,从最早的Evans的著作开始,就对这个问题非常重视,提供了很多最佳实践与技术解决方案作为参考(详见第7章)。
(4)开发与测试之间
领域模型的内聚性和独立性,以及模型与代码的一致性,使得单元测试与静态测试可以方便地进行。领域专家和用户也可以自行编写单元测试用例,进行代码走查。
基于“测试越早,问题修复的代价就越小”的测试原则,以及敏捷与DevOps方法对自动化快速测试的强依赖性,DDD的价值怎么强调都不过分。更进一步,如果你能理解测试驱动开发(Test Driven Development, TDD)其实是一种设计方法,并且打算采用TDD,那么与DDD可以说是天作之合(见第2章)。
DDD在对上述每一对关系协作的增效上都有出色表现,可以说有颠覆性的革新。三合一与领域模型的独立、内聚的特性,消除了团队沟通中的各种迷雾。每种角色都能在以领域模型为核心的生态里,找到自己的定位。各角色之间没有团队墙和信息差,这无疑是一种科学的做法。