“每个有思想的软件开发人员,书架上都应该有这样一本书”
——Kent Beck
领域驱动设计
——软件核心复杂性应对之道
Eric Evans 著,清华大学出版社即将出版中译本,陈大峰,张泽鑫
译
UMLChina训练辅助教材

前言
从领先的软件设计人员开始将领域建模及设计视为关键性课题到现在已经有至少20年的时间了,然而令人惊讶的是几乎没有相关的应该做什么和如何做的文献出现过。尽管领域建模和设计并没有被明确地形式化,然而在对象领域中出现了一种潜在的哲学体系,它就是我所说的领域驱动设计(domain-driven design)。
我花费十年开发了几个商业和技术领域的复杂系统。在工作过程中,我尝试了几种已经出现在面向对象开发前沿领域的设计与开发程序。这些项目中有一些非常成功,也有少数的几个最终失败。成功项目的共同特征是在迭代的设计中不断地完善领域模型并将它作为项目的骨干结构的一部分。
本书提供了进行设计决策的框架和讨论领域设计时使用的技术词汇表,集中了被广泛接受的优秀实践,这些案例都是在我自己的领域与工作中的经验积累。需要面对复杂领域的软件开发团队可以使用这个框架来系统地进行领域驱动设计。
1. 比较三个项目
在我的记忆中有三个项目能够作为说明动态领域建模设计如何影响开发结果的生动实例。尽管这三个项目都交付了实用的软件,然而只有一个达到了优秀的目标,并且生成了能够根据组织不断发展的要求进行持续完善的复杂软件。
我注意到一个非常迅速地提交了简单实用的基于Web的贸易系统的项目。开发者们任凭自己的感觉进行开发,但这种态度并没有对他们的工作造成阻碍,因为一个简单软件的编写并不需要注意设计问题。初始成功的结果是,对于未来继续开发的要求极高。这时我被要求进入第二版本的开发工作。仔细地研究了这个项目后,我发现他们缺少一个领域模型,甚至连项目通用的语言都没有,整个设计处于无结构状态。项目的领导者不同意我的论断,于是我拒绝了这个工作。一年后,这个开发团队陷入困境,无法提交第二个版本。尽管他们对技术的使用方式并无什么错误,从商业逻辑来看我们还是应该克服这种情况。他们的第一个版本过早地固化导致了高额的维护代价。
处理这种高度复杂的问题要求对领域逻辑的设计采用更加认真的方法。在我事业的早期,非常幸运地能够完成一项格外重视领域设计的项目。这项项目的复杂性不小于前面提到的第一个任务,也同样是一开始向制造商提交了一个简单的应用软件,适度地完成初始工作。但是这次情况有所不同,初始提交的版本不断地加速开发。每一次迭代都会对前一个版本功能的整合与细化提出令人兴奋的新意见。开发团队能够灵活地进行扩展以反馈制造商的要求。这种向上的良好发展轨迹直接归功于一个明确的领域模型,迭代地改进并快速地编码。随着团队对领域更进一步的洞察,模型也随之进一步深化。开发者之间甚至开发者与领域专家之间的交流得以改善,项目的设计也不像以前那样带来艰巨的维护任务,而变得易于修改和扩展了。
遗憾的是,项目并不会仅仅因为认真进行建模而进入一个良性循环。我过去接触过一个项目,开始时基于领域模型是要建立一个全球企业系统,但是经过几年屡屡受挫,不得不降低目标而落入俗套。这个团队有良好的工具与对业务的深入理解,并且对于建模也格外重视。然而拙劣的开发角色划分使得模型与实现相互分离,因此设计并没有反映出分析的深度。在任何情况下,详细的业务对象设计并不是保证它们在复杂精细项目中完美结合实现的充分条件。再三的迭代并不能够提高编码质量,因为开发者之间技术水平不均衡,而他们又不了解面向实际的运行软件而建立基于模型的对象的技术与其本身的风格。时间一点点过去,开发工作陷入复杂的泥潭,团队也丧失了对系统的整体把握。经过几年的工作,该项目并没有生产出有用的软件,该团队却不得不放弃其初始时建模的目标。
2. 复杂度的难题
很多原因会导致一个项目偏离正确轨道:官僚主义、不明确的目标、资源的匮乏以及其他诸多因素。但是设计能够很大程度上决定软件的复杂度。当复杂度变得难以控制时,开发者便不再能够很好地理解软件,从而也不能够方便和安全地对它进行改变与扩展。另一方面,一个优秀的设计会为开发这些复杂特性带来机会。
一些设计因素是技术上的,在网络、数据库和其他软件技术方面的设计已经有了很多研究,这些问题的解决方法也有许多书籍专门论述。开发者们不断提升自己的技能并紧跟着每一次的技术进步。
然而许多应用程序最大的复杂性不在技术方面,而是在领域本身用户的活动或业务方面。一个成功的设计必须系统地处理软件的这个核心方面。
本书的前提有两个:
(1)对于大多数的软件项目,主要的焦点应该在领域及领域逻辑方面。
(2)复杂的领域设计应该基于一个模型来进行。
领域驱动的设计既是一种思考的方式也是一系列的优先级,目的在于加速处理复杂领域的软件项目。为了达到这个目标,本书提供了数量巨大的设计实践、技巧和原则。
3. 设计 vs. 开发过程
设计方面的书籍与过程方面的书籍很少相互提及,其中每个主题本身就是一个很复杂的问题。本书是一本关于设计方面的著作,但是我认为设计与过程是不可分割的。设计理念必须得以成功实现,否则它们将仅仅止步于学术讨论。
在人们学习设计技术时,常常会为各种可能性感到兴奋不已。接着他们会遇到实际项目的杂乱现状,他们无法将新的设计思路应用在必须使用的技术上。或者是他们不知道何时应该抛开设计方面的局限,何时又应该严格地遵循设计寻找一个正确的解决方案。开发者们相互讨论抽象的应用程序设计原则,但更为实际的是讨论关于现实的项目如何完成。因此,尽管这是一本设计书籍,在需要时我仍然会涉及到过程领域的知识。这样做更有助于在合适的上下文中讨论设计原则。
本书并不局限于某一特定的方法学,然而它是面向新的“敏捷开发过程”家族的。它假设项目实践中有几个约定俗成的惯例。下面两个惯例是本书所采用方法的先决条件。
(1)开发工作是迭代的。迭代开发过程已经被提倡并实践了几十年,它是敏捷开发方法的基础。关于敏捷开发和极限编程(或XP)的文献有很多讨论,如Surviving Object-Oriented Projects(Cockburn 1998)和Extreme Programming Explained(Beck 1999)。
(2)开发者与领域专家之间关系密切。领域驱动设计需要大量深入的领域知识以及对于其中关键概念的关注。这是一项了解领域与了解如何建立软件的人们之间的合作。因为开发工作是迭代进行的,因此这种合作要始终贯穿于项目的生命周期。
Kent Beck、Ward Cunningham和其他人认为(参见Extreme Programming Explained [Beck 2000])最后的编程实现是敏捷开发过程中最重要的,也是我最经常需要处理的工作。在本书中,为了更加具体地阐述,我使用XP作为设计与过程交互讨论的基础。举例所用的原则可以方便地适用于其他的敏捷开发过程。
最近几年人们开始质疑精细开发方法学,认为它们产生了无用的、静态的文档以及强制性的前置计划和设计。相反的,敏捷开发过程,例如XP,则强调对变化和不确定性的应对。
极限编程承认设计决策的重要性,但是它极力反对前置设计。它花费大量精力进行交流和提高项目的迅速转向能力。有了这样的反应能力,开发者可以在项目的每个阶段使用“可运行的最简单事物”然后进行持续重构,进行许多小的设计改善,最后完成符合用户需求的设计。
这种极保守行为对于一些过度的设计狂热者是一种非常必要的解药。那些使得项目举步维艰的大量文档并没有什么价值。由于开发团队生怕完成不完善的设计,使这些项目深受“分析瘫痪”之苦。这种情况必须要有所改变。
不幸的是,这些开发过程的观念常常被曲解。每个人对于“简单”都有不同的定义。持续的重构是一系列小型的再设计;那些缺乏一致的设计原则的开发者将产生出难于理解或修改的代码——而这恰恰与敏捷背道而驰。尽管由于对各种无法预料的需求的担心常常导致过度工程(overengineering),然而试图避免过度工程却可能导致另一种担心:不敢进行任何深入的设计思考。
实际上,XP最适合具有敏锐设计感知的开发者。XP过程假设你能够通过重构完善一个设计,并且你会频繁而迅速地进行重构。但是前面的设计选择会使得重构本身变得更容易或更困难。XP过程尝试增加团队交流,然而不同的模型和设计选择会使交流变得明确或混淆。
本书将设计与开发实践结合在一起,并且举例说明领域驱动设计与敏捷开发如何相互补充。在敏捷开发过程环境中精细的领域建模方法能够加速开发。与领域开发过程之间的相互关系使得这种方法比其他真空下考虑的“纯”设计方法更加实际。
4. 本书的结构
本书分为四个主要部分:
第一部分:让领域模型发挥作用介绍了领域驱动开发的基本目标;这些目标推动后面几章的实践。由于软件开发有很多方法,第1章定义了一些术语并说明了使用领域模型驱动交流和设计的总的看法。
第二部分:模型驱动设计的构成元素将面向对象领域建模实践的核心浓缩为一些基本的构件。这部分着力于在模型与实际之间搭建桥梁并运行软件。这些标准模式的共享使得设计得以有序进行,团队成员更加容易了解彼此的工作。使用标准模式更有助于使用通用语言的术语,这样所有的团队成员便可以使用它们来讨论模型和设计决策。
但是本部分主要关注的是保持模型与实现之间相互调整并提高效率的决策种类。这种调整需要注意到单个元素的细节。在这种小规模上的工作为开发者采用第三和第四部分的建模方法提供了稳定的平台。
第三部分:面向更深层理解的重构超越了构件范围而着力于将它们装配成可见结果的实际模型。本部分不是直接突变到深奥的设计原则,而是着重讨论发现过程。有价值的模型通常不会立刻产生,它们需要对领域的深入理解。基于幼稚的模型实现初始的设计,然后一次又一次地改变它。团队每次增加对领域的理解后,模型将被改变以反映进一步获得的知识,代码也随之重构来反映更深层次的模型,潜在地使其更接近可用的应用程序。因此,偶尔的困难可能正是突破到一个更加深入的模型,获得更完美设计的机会。
探索与生俱来就是可扩展的,然而它并不是随机的。第三部分主要研究能够在探索过程中指导决策的原则和帮助引导研究的技术。
第四部分:策略设计处理复杂系统、较大型机构以及外部系统与遗传系统交互中发生的各种情况。这部分提出了整体应用于系统的原则三元组:设备环境、精髓和大规模结构。战略性设计决策由团队制定,它们使得第一部分的目标大体实现,可能是适合企业范围网络的巨大系统或应用程序。
本书从始至终都使用提取自实际项目的真实例子说明各种论述,而不是用过于简单的“玩具”问题。
本书是按照一组模式书写的。读者不用关心这些模式就能够理解所有的材料,如果对模式的风格和格式感兴趣的读者可以阅读附录部分。
补充的材料可以在http://domaindrivendesign.org找到,其中包括附加的例子代码和团体讨论。