领域驱动设计 15-17章(本书完结)
15.精炼
如何才能专注于核心问题而不被大量的次要问题淹没呢?分层架构可以把领域概念从技术逻辑中(技术逻辑确保了计算机系统能够运转)分离出来,但在大型系统中,即使领域被分离出来,它的复杂性也可能仍然难以管理。
精炼是把一堆混杂在一起的组件分开的过程,以便从中提取出最重要的内容,使得它更有价值,也更有用。模型就是知识的精炼。通过每次重构得到更深层的理解。
像很多化学蒸馏过程一样,精炼过程(例如通用子域 generic subdomain 和内聚机构 coherent mechanism)所分离出来的副产品本身也很有价值,但精炼的主要动机是把最有价值的那部分提取出来,正是这个部分使我们的软件区别于别的软件,而且由于这个部分的存在才值得我们去构建软件,这个部分就叫做 核心域 core domain
15.1 模式:核心域
在设计大型系统时,有很多有用的组件,它们都很复杂而且绝对有必要把它们做好,这导致真正的业务资产-领域模型-被掩盖和忽略了。
一个严峻的现实是我们不可能对所有设计部分进行同等的精化,而是必须分出优先级。为了使领域模型成为有价值的资产,必须整齐地模型的真正核心,并完全根据这个核心来创建应用程序的功能。但本来就稀缺的高水平开发人员往往会把工作重点放在技术基础设施上,或者只是去解决那些不需要专门领域知识就能理解的领域问题
因此:
对模型进行提炼。找到core domain并提供一种易于区分的方法把它与那些其辅助作用的模型和代码分开。最有价值和最专业的概念要轮廓分明。尽量压缩核心域、
让最有才能的人来开发核心域,并相应地招募新人来补充这些人空出来的位置。在核心域中努力开发能够确保实现系统蓝图的深层模型和柔性设计。仔细判断任何其他部分的投入,看它是否能够支持这个提炼出来的core。
1. 选择核心
对核心域的选择取决于看问题的角度。例如,很多应用程序需要一个通用的货币模型,用来标识各种货币以及它们的汇率和兑换。另一方面,一个用来支持货币交易的应用程序可能需要更精细的货币模型,这个模型有可能就是核心的一部分。即使在这种情况下,货币模型中可能有一部分仍是非常通用的。随着对领域理解的不断加深,精炼过程可以持续进行,这需要把通用的货币概念分离出来,而只把模型中那些专有的部分保留在核心域中。
一个应用程序的核心域在另一个应用程序中可能只是通用的支持组件
2. 工作的分配
恶性循环:技术能力强的人往往缺乏丰富的领域知识
方法:建立一支由开发人员和一位或多位领域专家组成的联合团队,其中开发人员必须能力很强、能够长期稳定地工作并且对学习领域知识非常感兴趣,而领域专家则要掌握深厚的业务知识。如果你认真对待领域设计,那么它就是一项有趣且充满技术挑战的工作。你肯定也会找到持这种观点的开发人员。
15.2 精炼的逐步提升
领域前景说明(domain vision statement):传达基本概念以及它们的价值
突出的核心(highlighted core):改善沟通,并指导决策指定过程,这也只需对设计进行很少的改动甚至无需改动
更积极的精炼方法是通过重构和重新打包显式地分离出通用子域(generic subdomain),然后分别进行处理,在使用内聚机制(cohesive mechanism)的的同时,也要保持设计的通用性、易懂性和柔性,这两个方面可以结合出来。只有除去了这些细枝末节,才能把核心剥离出来
重新打包出隔离的核心(segregated core),可以使得这个核心清晰可见,并且可以促进将来在核心模型上的工作
最积极的精炼技术是抽象内核(abstract core),它用抽象的形式表示了最基本的概念和关系(需要对模型进行全面的重新组织和重构)
每种技术都需要我们连续不断地投入越来越多的工作,但刀磨得越薄,就会越锋利。领域模型的连续精炼将为我们创造一项资产,使项目进行得更快、更敏捷、更精确
首先,我们可以把模型中最普通的那些部分分离出去,它们就是通用子域
15.3 模式:通用子域
例子:企业组织图,会计模型
把内聚的子领域(它们不是项目的动机)识别出来。把这些子领域的通用模型提取出来,并放到单独的module中。任何专有的东西都不应放在这些模块中。
把它们分离出来以后,在继续开发的过程中,它们的优先级应低于核心域的优先级。此外,还可以考虑为这些通用子域使用现成的解决方案或"公开发布的模型"(published model)
1. 通用不等于可以重用
如果把行业专用的模型元素引入到通用子领域中,会产生两个后果。
第一,它将妨碍将来的开发。虽然,现在我们只需要子领域模型的一小部分,但我们的需求会不断增加。如果把任何不属于子领域概念的部分引入到设计中,那么再想灵活地扩展系统就很难了,除非完全重建原来的部分并重新设计使用该部分的其他模块
第二,也是更重要的,这些行业专用的概念要么属于核心域,要么属于它们自己的更专业的子领域,而且这些专业的模型比通用子领域更有价值
2. 项目风险管理
敏捷过程通常要求通过尽早解决最具风险的任务来管理风险
15.4 模式:领域前景说明
:关注的重点是领域模型的性质,以及如何为企业带来价值。在项目开发的所有阶段,管理层和技术人员都可以直接用领域前景说明来指导资源分配、建模选择和团队成员的培训。
领域前景说明可以用作一个指南,它帮助开发团队在精炼模型和代码的过程中保持统一的方向。团队中的非技术成员、管理层甚至是客户也都可以共享领域前景说明
15.5 模式:突出核心
1. 精炼文档
创建一个单独的文档来描述和解释核心域
独立文档带来的所有常见风险也会在这里出现:
1)文档可能得不到维护
2)文档可能没人阅读
3)由于有多个信息来源,文档可能达不到简化复杂性的目的
控制这些风险最好方法是保持绝对的精简。剔除那些不重要的细节,只关心核心抽象以及它们的交互,这样文档的老化速度就会减慢
2. 标明核心
200页的领域模型文档(保险公司)
大幅删减,走查文档,找到一位懂得大量保险业知识的业务分析师帮助
3. 把精炼文档作为过程工具
15.6 模式:内聚机制
封装机制是面向对象设计的一个基本元祖。把复杂算法隐藏到方法中,再为方法起一个一看就知道其用途的名字,这样就把“做什么”和"如何做"分开了。这种技术使设计更易于理解和使用。
计算有时会非常复杂,使设计开始变得膨胀。机制性的"如何做"大量增加,而把概念性的"做什么"完全掩盖了。为解决问题提供算法的大量方法掩盖了那些用于表达问题的方法。
把概念上的内聚机制分离到一个单独的轻量级框架中。要特别注意公式算法或那些有完备文档的算法。有一个意图提示接口来公开这个框架的功能。现在,领域中的其他元素就可以只专注于如何表达问题(做什么)了,而把解决方案的复杂细节(如何做)转移给了框架。
1. 通用子域与内聚机制的比较
通用子域:描述问题的模型作为基础,它用这个模型表示出团队会如何看待领域的某个方面
内聚机制:解决描述性模型所提出来的一些复杂的计算问题
2. 机制是核心域的一部分
机制本身是一个专有部分并且使软件的一项核心价值
绕弯路之后又返回到原来的老路上是很常见的事情,但并不会退回到起点。最终结果通常是得到了一个更深层的模型,这个模型能够更清楚地区分出事实、目标和机制。实用的重构在保留中间阶段的重要价值的同时还能够去除不必要的复杂性。
15.7 通过精炼得到声明式风格
15.8 模式:隔离的核心
通过重构得到隔离核心的一般步骤如下所示:
1)识别出一个核心子域
2)把相关的类移动到新的module中,并根据与这些类有关的概念为模块命名
3)对代码进行重构,把那些不直接表示概念的数据和功能分离出来。把分离出来的元素放到其他包的类中。
4)对新的隔离核心进行重构,使其中的关系和交互变得更简单、表达得更清楚,并且最大限度地减少并澄清它与其他module的关系
5)对另一个核心子域重复这个过程,直到完成隔离核心的工作
1. 创建隔离核心的代价
当系统有一个很大的、非常重要的限界上下文时,如果模型的关键部分被大量支持性功能掩盖了,那么就需要创建隔离核心了
2. 不断发展演变的团队决策
把货物运输模型的核心隔离出来
运输模型的实质是什么?货物处理:根据客户需求来运输货物。我们需要把重点放在货物处理上:根据客户需求来运输货物。我们把与这些活动直接相关的类提取出来放到一个新的包Delivery中,这样就产生了一个segregated core
15.9 模式:抽象内核
15.10 深层模型精炼
15.11 选择重构目标
1)哪痛治哪。观察一下根源问题是否涉及核心域或核心域支持元素的关系,如果确实涉及,那么就要接受挑战,首先修复核心
2)当可以自由选择重构的部分时,应首先集中精力把核心域更好地提取出来,完善对核心的隔离,并且把支持性的子域提炼成通用子域
16. 大比例结构
:是一种语言,人们可以用它来从大局上讨论和理解系统
设计一种应用于整个系统的规则(或角色和关系)模式,使人们可以通过它在一定程度上了解各个部分的整体中所处的位置(即使是在不知道各个部分的详细职责的情况下)
16.1 模式:演进的顺序
evolving order
一个没有任何规则的随意设计会产生一些无法理解整体含义且很难维护的系统。但架构中预先设定的假设又会使项目变得束手束脚,而且会极大地限制应用程序中某些特定部分的开发人员/设计人员的能力。很快,开发人员就会为适应结构而不得不在应用程序的开发商委曲求全,要么就是完全推翻架构而又回到不协调的开发的老路上来。
应该允许这种概念上的大比例结构随着应用程序一起演变,甚至可以变成一种完全不同的结构风格。有些设计决策和模型决策必须在掌握了详细知识之后才能确定,这样的决策不必过早地制定
16.2 模式:系统隐喻
system metaphor
软件设计往往非常抽象且难于掌握。开发人员和用户都需要一些切实可行的方式来理解系统,并共享系统的一个整体视图。
16.3 模式:责任层
responsibility layer
所谓的层,就是对系统进行划分,每个层的元素都知道或能够使用在它"下面"的那些层的服务,但却不知道它"上面"的层,而且与它上面的层保持独立。低层的对象不依赖于高层的对象
松散分层系统 relaxed layered system
作业层(焦点)
能力层(潜能层/基础层)
决策支持层(用于制定计划和决策)
策略层(规则和目标是被动的,但它们约束着其它层的行为)
16.4 模式:知识级别
knowledge level
知识级别是反射模式在领域层的一种应用,反射模式能够使软件具有"自我感知"的特性,并使所选中的结构和行为可以接受调整和修改,从而满足变化需要。
基础级别(base level):承担应用程序的操作职责
元级别(meta level):表示有关软件结构和行为方面的知识
创建一组不同的对象,用它们来描述和约束基本模型的结构和行为。把这些对象分为两个"级别",一个是非常具体的级别,另一个级别则提供了一些可供用户或超级用户定制的规则和知识
16.5 模式:可插拔式组件框架
pluggable component framework
16.6 结构应该有一种什么样的约束
16.7 通过重构得到更适当的结构
1.最小化
2.沟通和自律
3.通过重构得到柔性设计
4.通过精炼可以减轻负担
17. 领域驱动设计的综合运用
17.1 把大比例结构与限界上下文结合起来使用
17.2 将大比例结构与精炼结合起来使用
17.3 首先评估
17.4 有谁制定策略
1.从应用程序开发自动得出的结构
2.以客户为中心的架构团队
17.5 制定战略设计决策的6个要点
决策必须传达到整个团队
决策过程必须收集反馈意见
计划必须允许演变
战略设计需要遵守简约和谦逊的原则
结束语
业务软件大不可必是拼凑而成的杂乱系统。与一个复杂的领域搏斗,把它转化为一个可理解的软件设计,这对于优秀的技术人员来说是一项激动人心的挑战
创建好的软件是一项需要学习和思考的活动。建模需要想象力和自律。好的工具能够帮助我们思考或避免分心。