领域建模已被业界普遍认为是软件设计成败的关键。通过领域建模,软件开发人员能够展示丰富的功能并将这些功能实现为真正满足用户需要的软件。尽管领域建模非常重要,但市面上介绍如何将有效的领域建模结合到软件开发过程中的著作却非常少。\r\n 本书就是为此目的而编写的。它向读者系统地讲述了领域驱动设计的方法,介绍了大量优秀的设计示例、技术经验以及用于处理复杂领域软件工程的基本原则。本书做到了设计和开发实践相结合,在介绍领域驱动设计的同时,还提供了大量的Java示例。\r\n 通过本书,读者将获得对领域驱动设计的总体认识,了解领域驱动设计中涉及的关键原则和术语。\r\n 面向对象的开发人员、系统分析师以及设计师在深入思考领域问题时,能够从本书中获得一定的指导,从而建立丰富而有用的领域模型,并将这些模型转化为高质量和持久的软件实现。
第Ⅰ部分 让领域模型发挥作用\r\n 第1章 消化知识\r\n 1.1 有效建模的因素\r\n 1.2 知识消化\r\n 1.3 持续学习\r\n 1.4 知识丰富的设计\r\n 1.5 深层模型\r\n 第2章 交流及语言的使用\r\n 2.1 通用语言\r\n 2.2 利用对话改进模型\r\n 2.3 一个团队,一种语言\r\n 2.4 文档和图\r\n 2.4.1 书面的设计文档\r\n 2.4.2 执行的基础\r\n 2.5 说明性模型\r\n 第3章 将模型和实现绑定\r\n 3.1 模型驱动设计\r\n 3.2 建模范型和工具支持\r\n 3.3 突出主旨:为什么模型对用户很关键\r\n 3.4 实践型建模人员\r\n第Ⅱ部分 模型驱动设计的构建块\r\n 第4章 分离领域\r\n 4.1 分层架构\r\n 4.1.1 层间的联系\r\n 4.1.2 架构框架\r\n 4.2 模型属于领域层\r\n 4.3 其他种类的隔离\r\n 第5章 软件中的模型描述\r\n 5.1 关联\r\n 5.2 实体(又称引用对象)\r\n 5.2.1 实体建模\r\n 5.2.2 设计标识操作\r\n 5.3 值对象\r\n 5.3.1 设计值对象\r\n 5.3.2 设计包含值对象的关联\r\n 5.4 服务\r\n 5.4.1 服务和分隔的领域层\r\n 5.4.2 粒度\r\n 5.4.3 访问服务\r\n 5.5 模块(包)\r\n 5.5.1 敏捷的模块\r\n 5.5.2 基础结构驱动打包的缺陷\r\n 5.6 建模范式\r\n 5.6.1 对象范式的优势\r\n 5.6.2 对象世界中的非对象\r\n 5.6.3 在混合范式中使用模型驱动设计\r\n 第6章 领域对象的生命周期\r\n 6.1 聚合\r\n 6.2 工厂\r\n 6.2.1 工厂及其应用场所的选择\r\n 6.2.2 只需构造函数的情况\r\n 6.2.3 接口的设计\r\n 6.2.4 如何放置不变量的逻辑\r\n 6.2.5 实体工厂与值对象工厂\r\n 6.2.6 存储对象的重建\r\n 6.3 仓储\r\n 6.3.1 查询仓储\r\n 6.3.2 了解仓储实现的必要性\r\n 6.3.3 实现仓储\r\n 6.3.4 在框架内工作\r\n 6.3.5 与工厂的关系\r\n 6.4 为关系数据库设计对象\r\n 第7章 使用语言:扩展示例\r\n 7.1 货物运输系统概述\r\n 7.2 隔离领域:系统简介\r\n 7.3 区分实体和值对象\r\n 7.4 运输领域中的关联设计\r\n 7.5 聚合的边界\r\n 7.6 选择仓储\r\n 7.7 场景概述\r\n 7.7.1 应用特性示例:改变一件货物的目的地\r\n 7.7.2 应用特性示例:重复业务\r\n 7.8 对象的创建\r\n 7.8.1 Cargo的工厂和构造函数\r\n 7.8.2 添加一个Handling Event\r\n 7.9 停下来重构:Cargo聚合的另一种设计\r\n 7.10 运输模型中的模块\r\n 7.11 引入新特性:配额检查\r\n 7.11.1 连接两个系统\r\n 7.11.2 改进模型:划分业务\r\n 7.11.3 性能调整\r\n 7.12 小结\r\n第Ⅲ部分 面向更深层理解的重构\r\n 第8章 突破\r\n 8.1 关于突破的故事\r\n 8.1.1 中看不中用的模型\r\n 8.1.2 突破\r\n 8.1.3 更深层的模型\r\n 8.1.4 冷静的决定\r\n 8.1.5 成效\r\n 8.2 时机\r\n 8.3 着眼于根本\r\n 8.4 尾声:一连串的新理解\r\n 第9章 隐含概念转变为显式概念\r\n ……\r\n 第10章 柔性设计\r\n 第11章 应用分析模式\r\n 第12章 把设计模式和模型联系起来\r\n 第13章 向更深层理解重构\r\n第Ⅳ部分 战略性设计\r\n 第14章 维护模型完整性\r\n 第15章 精炼\r\n 第16章 大比例结构\r\n 第17章 综合应用战略性设计\r\n 第18章 尾声\r\n附录A 关于模式\r\n附录B 术语表\r\n附录C 参考文献\r\n附录D 关系图
陈大峰,国防科技大学计算机与技术博士,研究方向;分布式计算;研究课题为过程集成工作流。对UML建模、EDOC、工作流和过程集成有深入的研究,曾发表多篇论文和专业文章。目前担任某消息代理中间件产品开发组长,一直使用UML作为设计工具和沟通工具,并取得显著成果。
前言
从领先的软件设计人员开始将领域建模及设计视为关键性课题到现在至少已经有20年的时间了,然而令人惊讶的是,几乎没有相关的文献来告诉大家应该做什么和如何做。尽管领域建模和设计并没有被明确地形式化,然而在对象领域中出现了一种潜在的哲学体系,它就是我所说的领域驱动设计(domain-driven design)。
我花费10年时间开发了几个商业和技术领域的复杂系统。在工作过程中,我尝试了几种已经出现在面向对象开发前沿领域的设计与开发程序。这些项目中有一些非常成功,也有少数几个最终失败。成功项目的共同特征是在迭代的设计中不断地完善领域模型并将它作为项目的骨干结构的一部分。
本书提供了进行设计决策的框架和讨论领域设计时使用的技术,集中了被广泛接受的优秀实践,这些案例都是在我自己的领域与工作中的经验积累。需要面对复杂领域的软件开发团队可以使用这个框架来系统地进行领域驱动设计。
比较3个项目
在我的记忆中,有3个项目能够作为说明动态领域建模设计如何影响开发结果的生动示例。尽管这3个项目都交付了实用的软件,然而只有一个达到了优秀的目标,并且生成了能够根据组织不断发展的要求进行持续完善的复杂软件。
我注意到一个非常迅速地提交了简单实用的基于Web的贸易系统的项目。开发人员们任凭自己的感觉进行开发,但这种态度并没有对他们的工作造成阻碍,因为一个简单软件的编写并不需要注意设计问题。初始成功的结果是,对于未来继续开发的要求极高。这时我被要求进入第2版本的开发工作。仔细地研究了这个项目后,我发现他们缺少一个领域模型,甚至连项目通用的语言都没有,整个设计处于无结构状态。项目的领导者不同意我的论断,于是我拒绝了这个工作。一年后,这个开发团队陷入困境,无法交付第2个版本。尽管他们对技术的使用方式并无什么错误,从业务逻辑来看,我们还是应该克服这种情况。他们的第1个版本过早地固化导致了高额的维护代价。
处理这种高度复杂的问题要求对领域逻辑的设计采用更加认真的方法。在我事业的早期,非常幸运地能够完成一项格外重视领域设计的项目。该项目的复杂性不小于前面提到的第一个任务,也同样是一开始向贸易商提交了一个简单的应用软件,适度地完成初始工作。但是这次情况有所不同,初始交付的版本不断地加速开发。每一次迭代都会对前一个版本功能的整合与细化提出令人兴奋的新意见。开发团队能够灵活地进行扩展以反馈贸易商的要求。这种向上的良好发展轨迹直接归功于一个明确的领域模型,迭代地改进并快速地编码。随着团队对领域更进一步的洞察,模型也随之进一步深化。开发人员之间甚至开发人员与领域专家之间的交流得以改善,项目的设计也不像以前那样带来艰巨的维护任务,而变得易于修改和扩展了。
遗憾的是,项目并不会仅仅因为认真进行建模而进入一个良性循环。我过去接触过一个项目,开始时基于领域模型是要建立一个全球企业系统,但是经过几年屡屡受挫,不得不降低目标而落入俗套。这个团队有良好的工具与对业务的深入理解,并且对于建模也格外重视。然而拙劣的开发角色划分使得模型与实现相互分离,因此设计并没有反映出分析的深度。在任何情况下,详细的业务对象设计并不是保证它们在复杂精细项目中完美结合的充分条件。再三的迭代并不能够提高编码质量,因为开发人员之间技术水平不均衡,而他们又不了解面向实际的运行软件而建立基于模型的对象的技术与其本身的风格。时间一点点过去,开发工作陷入复杂的泥潭,团队也丧失了对系统的整体把握。经过几年的工作,该项目并没有生产出有用的软件,该团队却不得不放弃其初始时建模的目标。
复杂度的难题
很多原因会导致一个项目偏离正确轨道:官僚主义、不明确的目标、资源的匮乏以及其他诸多因素。但是设计能够在很大程度上决定软件的复杂度。当复杂度变得难以控制时,开发人员便不再能够很好地理解软件,从而也不能够方便和安全地对它进行改变与扩展。另一方面,一个优秀的设计会为开发这些复杂特性带来机会。
一些设计因素是技术上的,在网络、数据库和其他软件技术方面的设计已经有了很多研究,这些问题的解决方法也有许多书籍专门论述。开发人员们不断提升自己的技能并紧跟着每一次的技术进步。
然而许多应用程序最大的复杂性不在技术方面,而是在领域本身,用户的活动或业务方面。如果在设计中没有处理好领域复杂性,那么基础设施技术再好也不管用。一个成功的设计必须系统地处理软件的这个核心方面。
本书的前提有两个:
● 对于多数软件项目,主要的焦点应该在领域及领域逻辑方面。
● 复杂的领域设计应该基于一个模型来进行。
领域驱动的设计既是一种思考的方式也是一系列的要优先考虑的事情,目的在于加速处理复杂领域的软件项目。为了达到这个目标,本书提供了大量的设计实践、技巧和原则。
设计与开发过程
设计方面的书籍与过程方面的书籍很少相互提及,其中每个主题本身就是一个很复杂的问题。本书是一本关于设计方面的著作,但是我认为设计与过程是不可分割的。设计理念必须得以成功实现,否则它们将仅仅止步于学术讨论。
在人们学习设计技术时,常常会为各种可能性感到兴奋不已。接着他们会遇到实际项目的杂乱现状,他们无法将新的设计思路应用在必须使用的技术上。或者是他们不知道何时应该抛开设计方面的局限,何时又应该严格地遵循设计,寻找一个正确的解决方案。开发人员们相互讨论抽象的应用程序设计原则,但更为实际的是讨论现实的项目如何完成。因此,尽管这是一本设计书籍,在需要时我仍然会涉及到过程领域的知识。这样做更有助于在合适的上下文中讨论设计原则。
本书并不局限于某一特定的方法学,然而它是面向新的“敏捷开发过程”系列的。它假设项目实践中有几个约定俗成的惯例。下面两个惯例是本书所采用方法的先决条件。
● 开发工作是迭代的。迭代开发过程已经被提倡并实践了几十年,它是敏捷开发方法的基础。关于敏捷开发和极限编程(或XP)的文献有很多讨论,如Surviving Object-Oriented Projects(Cockburn 1998)和Extreme Programming Explained(Beck 1999)。
● 开发人员与领域专家之间关系密切。领域驱动设计需要大量深入的领域知识以及对于其中关键概念的关注。这是一项了解领域与了解如何建立软件的人们之间的合作。因为开发工作是迭代进行的,因此这种合作要始终贯穿于项目的生命周期。
Kent Beck、Ward Cunningham和其他人认为(参见Extreme Programming Explained [Beck 2000])最后的编程实现是敏捷开发过程中最重要的,也是我最经常需要处理的工作。在本书中,为了更加具体地阐述,我使用XP作为设计与过程交互讨论的基础。举例所用的原则可以方便地适用于其他的敏捷开发过程。
最近几年,人们开始质疑精细开发方法学,认为它们产生了无用的、静态的文档以及强制性的前置计划和设计。相反,敏捷开发过程,例如XP,则强调对变化和不确定性的应对。
极限编程承认设计决策的重要性,但是它极力反对前置设计。它花费大量精力进行交流和提高项目的迅速转向能力。有了这样的反应能力,开发人员可以在项目的每个阶段使用“可运行的最简单事物”,然后进行持续重构,进行许多小的设计改善,最后完成符合用户需求的设计。
这种极保守行为对于一些过度的设计狂热者是一种非常必要的解药。那些使得项目举步维艰的大量文档并没有什么价值。由于开发团队害怕所做的设计不完善,使这些项目深受“分析瘫痪”之苦。这种情况必须要有所改变。
遗憾的是,这些开发过程的观念常常被曲解。每个人对于“简单”都有不同的定义。持续的重构是一系列小型的再设计;那些缺乏一致的设计原则的开发人员将编写出难以理解或修改的代码——而这恰恰与敏捷背道而驰。尽管由于对各种无法预料的需求的担心常常导致过度工程(overengineering),然而试图避免过度工程却可能导致另一种担心:不敢进行任何深入的设计思考。
实际上,XP最适合具有敏锐设计感知的开发人员。XP过程假设您能够通过重构完善一个设计,并且您会频繁而迅速地进行重构。但是前面的设计选择会使得重构本身变得更容易或更困难。XP过程尝试增加团队交流,然而不同的模型和设计选择会使交流变得明确或混淆。
本书将设计与开发实践结合在一起,并且举例说明领域驱动设计与敏捷开发如何相互补充。在敏捷开发过程环境中精细的领域建模方法能够加速开发。与领域开发过程之间的相互关系使得这种方法比其他凭空考虑的“纯”设计方法更加实际。
本书结构
本书分为4个主要部分:
第Ⅰ部分:让领域模型发挥作用。介绍了领域驱动开发的基本目标;这些目标推动后面几章的实践。由于软件开发有很多方法,第1章定义了一些术语并说明了对使用领域模型驱动交流和设计的总的看法。
第Ⅱ部分:模型驱动设计的构建块。将面向对象领域建模实践的核心浓缩为一些基本的构建块。这部分着力于在模型与实际之间搭建桥梁并运行软件。这些标准模式的共享使得设计得以有序进行,团队成员更加容易了解彼此的工作。使用标准模式更有助于使用通用语言的术语,这样所有的团队成员便可以使用它们来讨论模型和设计决策。
但是本部分主要关注的是保持模型与实现之间相互协调以及提高效率的决策种类。这种协调需要注意到单个元素的细节。在这种小规模上的工作为开发人员采用第Ⅲ和第Ⅳ部分的建模方法提供了稳定的平台。
第Ⅲ部分:面向更深层理解的重构。超越了构建块范围而着力于将它们装配成可见结果的实际模型。本部分并没有直接介绍深奥的设计原则,而是着重讨论发现过程。有价值的模型通常不会立刻产生,它们需要对领域进行深入理解。基于幼稚的模型实现初始的设计,然后一次又一次