《领域驱动设计》学习笔记

【第一部分】运用领域模型


第1章:消化知识

有效的建模要素

(1)模型和实现的绑定算法

(2)创建了一种基于模型的语言数据库

(3)开发一个蕴含丰富知识的模型编程

(4)提炼模型设计模式

(5)头脑风暴和实验安全

 

【学习心得】:千万不要用本身有限的思惟规划完整的图形,持续学习、消化、输出(讨论)、沉淀,全部道理都是一致的。服务器

 

第2章:交流语言与使用

模式:UBIQUITOUS LANGUAGE(通用语言)


术语的交集产生了UBIQUITOUS LANGUAGE

想要建立种灵活的、蕴含丰富知识的设计,须要一种通用的、共享的团队语言,以及对语言不断的试验——然而,软件项目上不多出现这样的试验。架构

若是语言支离破碎,项目必将遭遇严重问题。领域专家使用他们本身的术语,而技术团队所使用的语言则通过调整,以便从设计角度讨论领域。平常讨论所使用的术语与代码(软件项目的最重要产品)中使用的术语不一致,甚至同一我的在讲话和写东西时使用的言语也不一致,这致使的后果是,对领域的深入表达经常稍纵即逝,根本没法记录到代码或文档中。翻译使得沟通不顺畅,并削弱了知识消化。然而任何一方的语言都不能成为公共语言,由于它们没法知足全部的需求。框架

 

【学习心得】:在本身有限的项目经验里,说沟通成本占据项目总成本的八成都不为过,就像本书一开始的重点,就是无处不在的语言。这语言能够是人话、能够是图形、能够是表格,重点在于能够帮助项目高质量高效率的落地。这里引用歌德的一句话:“世界上的误解和懈怠,也许比奸诈和恶意更误事”。数据库设计

 

第3章:绑定模型和实现

模式:MODEL-DRIVEN-DESIGN

 


模型-范式-设计

严格按照基础模型来编写代码,可以使代码更好地表达设计含义,而且使模型与实际的系统想契合。编程语言

若是整个程序设计或者其核心部分没有与领域模型相对应,那么这个模型就是没有价值的,软件的正确性也值得怀疑。同时,模型和设计功能之间过于复杂的对应关系也是难于理解的,在实际项目中,当设计改变时也没法维护这种关系。若分析与设计之间产生严重分歧,那么在分析和设计活动中所得到的知识就没法彼此共享。

软件系统各个部分的设计应该忠实地反映领域模型,以便体现出这两者之间的明确对应关系。咱们应该反复检查并修改模型,以便软件能够更加天然地实现模型,即便想让模型反映出更深层次的领域概念时也应如此。咱们须要的模型不但应该知足这种需求,还应该可以支持健壮的UBIQUITOUS LANGUAGE(通用语言)。

从模型中获取用于程序设计和基本职责分配的术语。让程序代码成为模型的表达,代码的改变可能会是模型的改变。而其影响势必要波及接下来相应的项目活动。

 

【学习心得】:模型、范式与设计的基本认知时候全部沟通的基石,不管是技术人员仍是领域业务人员都有必要对这些知识有一个深刻的理解,切记把本身局限在本身的细节当中,用人话讲就是钉子思惟。对其余工做小组的认识是一种促进你们更好合做的责任心态度,仍是那句话,用宏观的视野作微观的事情。

 

【 第二部分 】模型驱动设计的构造块


第4章:分离领域

模式:LAYERED ARCHTECTURE


分层模式

想要建立出可以处理复杂任务的程序,须要作到关注点分离——使设计中的每一个部分获得单独的关注。在分离的同时,也须要维持系统内部复杂的交互关系。

分层的价值在于每一层都只表明程序中的某一特定方面的。这种限制使每一个方面的设计都更具内聚性,更容易理解。

而领域层是模型的精髓。领域模型是一些列概念的集合。“领域层”则是领域模型以及全部与其直接相关的设计元素的表现,他由业务逻辑的设计和实现组成。在MODEL-DRIVEN-DESIGN中,领域层的软件构造反映出了模型概念。

 

【学习心得】:分离意味着原始的复杂,这是发展的一个趋势,技术的进步每每在于精细化的分工,而这种分层的另外一个好处是,分离核心,聚焦问题。

 

第5章:软件中所表示的模型

模式:ENTITY(又称为REFERENCE OBJECT)

一些对象主要不是由它们的属性定义的。它们实际上表示了一条“标识线”(A Thread of Identity),这条线跨越时间,并且经常经历多种不一样的表示。有时,这样的对象必须与另外一个具备不一样属性的对象相匹配。而有时一个对象必须与具备相同属性的另外一个对象区分开。错误的标识可能会破坏数据。

当一个对象由其标识(而不是属性)区分时,那么在模型中应该主要经过标识来肯定该对象的定义。是类定义变得简单,并集中关注生命周期的连续性和标识。定义一种区分每一个对象的方式,这种方式应该与其形式和历史无关。要格外注意那些须要经过属性来匹配对象的需求。在定义标识操做时,要确保这种操做为每一个对象生成惟一的结果,这能够经过附加一个保证惟一性的符号来实现。这种定义标识的方法可能来自外部,也多是由系统建立的任意标示符,但它在模型中必须是惟一的标识。模型必须定义“符合什么条件才算是相同的事物”。

 

模式:VALUE OBJECT

不少对象没有概念上的标识,它们描述了一个事务的某种特征。而这类用于描述领域的某个方面而自己没有概念标识的对象称为VALUE OBJECT(值对象)。

当咱们只关心一个模型元素的属性时,应把它归类为VALUE OBJECT。咱们应该使这个模型元素可以表示出其属性的意义,并为它提供相关功能。VALUE OBJECT应该是不可变的。不要为它分配任何标识,并且不要把它设计成像ENTITY那么复杂。

 

模式:SERVICE

有时,对象不是一个事物。在某些状况下,最清楚、最实用的设计会包含一些特殊的操做,这些操做从概念上讲不属于任何对象。与其把它们强制地归于哪一类,不如顺其天然地在模型中引入一种新的元素,这就是SERVICE(服务)。

所谓SERVICE,它强调的是与其余对象的关系。与ENTITY和VALUE OBJECT不一样,它只是定义了可以为客户作什么。SERVICE每每是一个一活动来命名,而不是以一个ENTITY来命名,也就是说,它是动词而不是名词。

好的SERVICE有如下3个特征:

(1)与领域概念相关的操做不是ENTITY或VALUE OBJECT的一个天然组成部分。

(2)接口是根据领域模型的其余元素定义的。

(3)操做是无状态的。

当领域中的某个重要的过程或转换操做不是ENTITY或VALUE OBJECT的天然职责时,应该在模型中添加一个做为独立接口的操做,并将其声明为SERVICE。定义接口时要使用模型语言,并确保操做名称是UBIQUITOUS LANGUAGE中的术语。此外,应该使SERVICE成为无状态的。

SERVICE与孤立的领域层:这种模式只重视那些在领域中具备重要意义的SERVICE,但SERVICE并不仅是在领域中使用。咱们须要注意区分属于领域层的SERVICE和那些属于其余层的SERVICE,并划分责任,以便将它们明确地区分开。


将SERVICE划分到各层中(资金转帐 示例)
 

模式:MODULE(也称为PACKAGE)

MODULE是一个传统的、较成熟的设计元素。虽然使用模块有一些技术上的缘由,但主要缘由倒是“认知超载”。MODULE为人们提供了两种观察模型的方式,一是能够在MODULE中查看细节,而不会被整个模型淹没,二是观察MODULE之间的关系,而不考虑其内部细节。

每一个人都会使用MODULE,但却不多有人它们看成模型中的一个成熟的组成部分。代码按照各类各样的类别进行分解,有时是按照技术架构来分割的,有时是按照开发人员的任务分工来分割的。甚至那些从事大量重构工做的开发人员也倾向于使用项目早期造成的一些MODULE。

众所周知,MODULE之间应该是低耦合的,而在MODULE的内部则是高内聚的。耦合和内聚的解释使得MODULE听上去像是一种技术指标,仿佛根据关联和交互的分布状况来机械地判断它们。然而,MODULE并不只仅是代码的划分,并且也是概念的划分。一我的一次考虑的事情是有限的(所以才要低耦合)。不连贯的思想和“一锅粥”似的思想一样难于理解(所以才要高内聚)。

所以:选择可以描述系统的MODULE,并使之包含一个内聚的概念集合。这一般会实现MODULE之间的低耦合,但若是效果不理想,则应寻找一种更改模型的方式来消除概念之间的耦合,或者找到一个能够做为MODULE基础的概念(这个概念先前可能被忽视了),基于这个概念组织的MODULE能够以一种有意义的方式将元素集中到一块儿。找到一种低耦合的概念组织方式,从而能够相互独立地理解和分析这些概念。对模型进行精化,直到能够根据高层领域概念对模型进行划分,同时相应的代码也不会产生耦合。MODULE的名称应该是UBIQUITOUS LANGUAGE中的术语。MODULE及其名称应反映出领域的深层知识。

 

【学习心得】:每个概念或方法,都有其含义来源和出处。学会寻找信息的源头,学会给本身的认知指明来源和出处,具有严谨的逻辑思惟,科学地学习和认知,是一切成功的基础。杜绝垃圾二手信息资料,杜绝自我局限性拍脑壳的认知决策过程。

 

第6章:领域对象的什么周期


领域对象的生命周期
 

模式:AGGREGATE

在具备复杂关联的模型中,要想保证对象更改的一致性是很困难的。不只互不关联的对象须要遵照一些固定规则,并且紧密关联的各组对象也要遵照一些固定规则。然而,过于谨慎的锁定机制又会致使多个用户之间毫无心义地互相干扰,从而使系统不可用。

固定规则(invariant)是指在数据变化时必须保持一致性规则,其涉及AGGREGATE成员之间的内部关系。而任何跨越AGGREGATE的规则将不要求每时每刻都保持最新状态。经过事件处理,批处理或其余更新机制,这些依赖会在必定时间内得以解决。但在每一个事务完成时,AGGREGATE内部所应用的固定规则必须获得知足。为了实现这个概念上的AGGREGATE,须要对全部事务应用一组规则:

□ 根ENTITY具备全局标识,它最终负责检查固定规则。

□ 根ENTITY具备全局标识。边界内的ENTITY具备本地标识,这些标识只在AGGREGATE内部才是惟一的。

□ AGGREGATE外部的对象不能引用除根ENTITY以外的任何内部对象。根ENTITY能够把对内部ENTITY的引用传递给它们,但这些对象只能临时使用这些引用,而不能保持引用。根能够把一个VALUE OBJECT的副本传递给另一个对象,而没必要关心它发生什么变化,由于它只是一个VALUE,再也不与AGGREGATE有任何关联。

□ 做为上一条规则的推论,只有AGGREGATE的根才能直接经过数据库查询获取。全部其余对象必须经过遍历关联来发现。

□ AGGREGATE内部的对象能够保持对其余AGGREGATE根的引用。

□ 删除操做必须一次删除AGGREGATE边界以内的全部对象。(利用垃圾回收机制,这很容易作到。因为除了根之外的其余对象都没有外部引用,所以删除了根之后,其余对象均会被回收。)

□ 当提交对AGGREGATE边界内部的任何对象的修改时,整个AGGREGATE的全部固定规则都必须知足。


本地标识与全局标识及对象引用

咱们应该将ENTITY和VALUE OBJECT分门类别地汇集到AGGREGATE中,并定义每一个AGGREGATE的边界。在每一个AGGREGATE中,选择一个ENTITY做为根,并经过根来控制对边界内其余对象的全部访问。只容许外部对象保持对根对象的引用。对内部成员的临时引用能够被传递出去,但仅在一次操做中有效。因为根控制访问,所以不能绕过它来修改内部对象。这种设计有利于确保AGGREAGATE中的对象知足全部固定规则,也能够确保在任何状态变化时AGGREGATE做为一个总体知足固定规则。

 

模式:FACTORY


与FACTORY的基本交互

当建立一个对象或建立整个AGGREGATE时,若是建立工做很复杂,或者暴露了过多的内部结果,则能够使用FACTORY进行封装。

对象的建立自己能够是一个主要操做,但被建立的对象并不适合承担复杂的装配操做。将这些职责混在一块儿可能产生难以理解的拙劣设计。让客户直接负责建立对象又会使客户的设计陷入混乱,而且破坏被装配对象或AGGREGATE的封装,并且致使客户与被建立对象的实现之间产生过于紧密的耦合。

所以:应该讲建立复杂对象的实例和AGGREGATE的职责转移给单独的对象,这个对象自己可能没有承担领域模型中的职责,但它还是领域设计的一部分。提供一个封装全部复杂装配操做的接口,并且这个接口不须要客户引用要被实例化的对象的具体类。在建立AGGREGATE时要把它做为一个总体,并确保它知足固定规则。

 

模式:REPOSITORY


REPOSITORY为客户执行一个搜索

在全部持久化对象中,有一小部分必须经过基于对象属性的搜索来全局访问。当很难经过遍历方式来访问某些AGGREGATE根的时候,就须要使用这种访问方式。它们一般是ENTITY,有时是具备复杂内部结构的VALUE OBJECT,还多是枚举VALUE。而其余对象则不宜使用这种方式,由于这会混淆它们之间的重要区别。随意的数据库查询会破坏领域对象的封装和AGGREGATE。技术基础设施和数据库访问机制的暴露会增长客户的复杂度,并妨碍模型驱动的设计。

REPOSITORY是一个简单的概念框架,它可用来封装这些解决方案,并将咱们的注意力从新拉回到模型上。REPOSITORY将某种类型的全部对象表示为一个概念集合(一般是模拟的)。它的行为相似于集合(collection),只是具备更复杂的查询功能。

所以:为每种须要全局访问的对象类型建立一个对象,这个对象至关于该类型的全部对象在内存中的一个集合的“替身”。经过一个众所周知的全局接口来提供访问。提供添加和删除对象的方法,用这些方法来封装在数据存储中实际插入或删除数据的操做。提供根据具体条件来挑选对象的方法,并返回属性值知足查询条件的对象或对象集合(所返回的对象是彻底实例化的),从而将实际的存储和查询技术封装起来。只为那些确实须要直接访问的AGGREGATE根提供REPOSITORY。让客户始终聚焦于模型,而将全部对象的存储和访问操做交给REPOSITORY来完成。

FACTORY与REPOSITORY的关系是:FACTORY负责处理对象生命周期的开始,而REPOSITORY帮助管理生命周期的中间和结束。从领域驱动设计的角度来看,FACTORY和REPOSITORY具备彻底不一样的职责。FACTORY负责制造新对象,而REPOSITORY负责查找已有对象。

 

【学习心得】:有时候学习上的困难不是由于本身的理解能力差,而是缺少必定的基础沟通语言。急于求成和半路出家的问题就在于基础的不扎实,也就是咱们所说的野路子。我曾经也会认为用到了再来学,这都是技术圈子的一个悖论。就好像等本身须要用钱了再来理财同样好笑。

 

第7章:使用语言:一个扩展的示例

 

【 第三部分 】经过重构来加深理解


第8章:突破


突破价值曲线

 

通常来讲,持续重构让事物逐步变得有序。代码和模型的每一次精化都让开发人员有了更加清晰的认识。这使得理解上的突破成为可能。以后,一系列快速的改变获得了更符合用户须要并更加切合实际的模型。其功能性及说明性急速加强,而复杂性却随之消失。这种突破不是某种技巧,而是一个事件。它的困难之处在于你须要判断发生了什么,而后再决定如何处理。

当突破带来更深层次的模型时,一般会使人感到不安。与大部分重构相比,这种变化的回报更多,风险也更高。并且突破出现的时机可能很不合时宜。尽管咱们但愿进展顺利,但每每事与愿违。过渡到真正的深层次模型须要从根本上调整思路,而且对设计作大幅修改。在不少项目中,建模和设计工做最重要的进展都来自于突破。

不要试图去制造突破,那只会使项目陷入困境。一般,只有在实现了许多适度的重构后才有可能出现突破。在大部分时间里,咱们都在进行微小的改进,而在这种连续的改进中模型深层含义也会逐渐显现。

要为突破作好准备,应专一于知识消化过程,同时也要逐渐创建健壮的UBIQUITOUS LANGUAGE。寻找那些重要的领域概念,并在模型中清晰地表达出来。精化模型,使其更具备柔性。提炼模型。利用这些更容易掌握的手段使模型变得更清晰,这一般会带来突破。

不要犹豫着不去作小的改进,这些改进即便脱离不开常规的概念框架,也能够逐渐加深咱们对模型的理解。不要由于好高骛远而使项目陷入困境。只要随时注意可能出现的机会就够了。

 

【学习心得】:我本身手头上目前持有一个运行了近十年的千万级用户系统,至今还在持续运营和追加功能。对于以上那些突破的感觉,我太有体会了。整个项目总共经历了两次大的突破,以及无数次小突破。而每一次突破都十分痛苦,但快乐着。离不开团队的坚持,离不开团队的持续学习,更离不开团队吃苦精神。

 

第9章:将隐式概念转变为显示概念

深层建模听起来很不错,可是咱们要如什么时候间它呢?深层模型之因此强大是由于它包含了领域的核心概念和抽象,可以以简单灵活的方式表达出基本的用户活动、问题以及解决方案。

若开发人员识别出设计中隐含的某个概念或者在讨论中收到启发而发现一个概念时,就会对领域建模和响应的代码进行许多转换,在模型中加入一个或多个对象或关系,从而将此概念显示地表达出来。有时,这种从隐式概念到显示概念的转换可能就是一次突破。

概念挖掘

一、倾听领域专家使用的语言。

有没有一些术语可以简洁地表达出复杂的概念?他们有没有纠正过你的用词(也许是很委婉的提醒)?当你使用某个特定词语时,他们脸上是否已经再也不流露出迷惑的表情?这些都是暗示了某个概念也许能够改进模型。

二、检查不足之处。

你所须要的概念并不老是浮在表面上,也毫不仅仅是经过对话和文档就能让它显现出来。有些概念可能须要你本身去挖掘和创造。要挖掘的地方就是设计中最不足的地方,也就是操做复杂且难于理解的地方。每当有新需求时,彷佛都会让这个地方变得更加复杂。有时,你很难意识到模型中丢失了什么概念。也许你的对象可以实现全部的功能,可是有些职责的实现却很笨拙。而有时,你虽然可以意识到模型中丢失了某些东西,可是却没法找到解决方案。

三、思考矛盾之处。

因为经验和需求的不一样,不一样的领域专家对一样的事情会有不一样的见解。即便是同一我的提供的信息,仔细分析后也会发现逻辑上不一致的地方。在挖掘程序需求的时候,咱们会不断遇到这种使人烦恼的矛盾,但它们也为深层模型的实现提供了重要线索。有时矛盾只是术语说法上的不一致,有些则是因为误解而产生的。但还有一种状况是专家们会给出相互矛盾的两种说法。

四、查阅书籍。

在寻找模型概念时,不要忽略一些显而易见的资源。在不少领域中,你均可以找到解释基本概念和传统思想的书籍。你依然须要与领域专家合做,提炼与你的问题相关的那部分知识,而后将其转化为适用于面向对象软件的概念。可是,查阅书籍也许可以使你一开始就造成一致且深层的认识。

五、尝试,再尝试。

并非全部这些方向性的改变都毫无用处。每次改变都会把开发人员更深入的理解添加到模型中。每次重构都使设计变得更灵活且为那些可能须要修改的地方作好准备。咱们其实别无选择。只有不断尝试才能了解什么有效什么无效。企图避免设计上的失误将会致使开发出来的产品质量劣质,由于没有更多的经验可用来借鉴,同时也会比进行一系列快速实验更加费时。

如何为那些不太明显的概念建模?

一、显示的约束。

约束是模型概念中很是重要的类别。它们一般是隐含的,将它们显式地表现出来能够极大地提升设计质量。


(例子)为显示表达超订策略而重构的模型  

二、将过程建模为领域对象。

首先要说明的是,咱们都不但愿过程变成模型的主要部分。对象是用来封装过程的,这样咱们只须要考虑对象的业务目的或意图就能够了。就像咱们以上用来安排货运路线的运输系统例子,安排路线的过程具备业务意义。SERVICE是显示表达这种过程的一种方式,同时它还会降异常复杂的算法封装起来。

若是过程的执行有多种方式,那么咱们也能够用另外一种方法来处理它,那就是将算法自己或其中的关键部分放到一个单独的对象中。这样,选择不一样的过程就变成了选择不一样的对象,每一个对象都表示一种不一样的STRATEGY(策略)。

那么,过程是应该被显示表达出来,仍是应该被隐藏起来呢?区分的方法很简单:它是常常被领域专家提起呢,仍是仅仅被看成计算机程序机制的一部分?

约束和过程是两大类概念模型,当咱们用面向对象语言编程时,不会当即想到它们,然而它们一旦被咱们视为模型元素,就真的可让咱们的设计更为清晰。

 

三、模式:SPECIFICATION


检查发票“谓词”提取例子

业务规则一般不适合做为ENTITY和VALUE OBJECT的职责,并且规则的变化和组合也会被掩盖领域对象的基本含义。可是将规则移出领域层的结果会更糟糕,由于这样一来,领域代码就再也不表达模型了。

逻辑编程提供了一个概念,即“谓词”这种可分离、可组合的规则对象,可是要把这种概念用对象彻底实现是很麻烦的。同时,这种概念过于通用,在表达设计意图方面,它的针对性不如专门的设计那么好。

所以:为特殊目的建立谓词形式的显式的VALUE OBJECT。SPECIFICATION就是一个谓词,可用来肯定对象是否知足某些标准。

 

【学习心得】:项目的沟通成本之大,正是由于许多人的心里都会有一颗“自尊心”,个人领域我最懂,个人技术牛逼,大家业务人员能够一边站。因此,想成为一个合格的团队成员,至少得让本身能成为一个合格的聆听者。看似简单,但我在生活中就发现了本身并不是一位很好的聆听者。错过了许多对本身有用的信息,更多仍是用了许多自觉得是的拍脑壳决策。

 

第10章:柔性设计


一些有助于得到柔性设计的模式  

为了使项目可以随着开发工做的进行加速前进,而不会因为它本身的老化将停滞不前,设计必需要让人们乐于使用,并且易于作出修改。这就是柔性设计(supple design)。

不少过分设计(overengineering)借着灵活性的名义而获得合理的外衣。可是,过多的抽象层和间接设计经常成为项目的绊脚石。看一下真正为用户带来强大功能的软件设计,你经常会发现一些简单的东西。简单并不容易作到。

 

模式:INTENTION-REVEALING INTERFACES(意图揭示接口)

若是开发人员为了使用一个组件而必需要去研究它的实现,那么就失去了封装的价值。当某我的开发的对象或操做被别人使用时,若是使用这个组件的新的开发者不得不根据其实现来推测其用途,那么他推测出来的可能并非那个操做或类的主要用途。若是这不是那个组件的用途,虽然代码暂时能够工做,但设计的概念基础已经被误用了,两位开发人员的意图也是背道而驰。

所以:在命名类和操做时要描述它们的效果和目的,而不是表露它们是经过何种方式达到目的的。这样能够使客户开发人员没必要去理解内部细节。这些名称应该与UBIQUITOUS LANGUAGE保持一致,以便团队成员能够迅速推断出它们的意义。在建立一个行为以前先为它编写一个测试,这样能够促使你站在客户开发人员的角度上来思考它。全部复杂的机制都应该封装到抽象接口的后面,接口只代表意图,而不代表方式。


新的方法名更能表达“油漆”有混合的做用
 

模式:SIDE-EFFECT-FREE FUNCTION(无反作用功能)

大多数操做都会调用其余的操做,然后者又会调用另一些操做。一旦造成这种任意深度的嵌套,就很难预测调用一个操做将要产生的全部后果。第二层和第三层操做的影响可能并非客户开发人员有意为之的,也是它们就变成了彻底意义上的反作用(任何对将来操做产生影响的系统状态改变均可以成为反作用)。

多个规则的相互做用或计算的组合产生的结果是很难预测的。开发人员在调用一个操做时,为了预测操做的结果,必须理解它的实现以及它所调用的其余方法的实现。若是开发人员不得不“揭开接口的面纱”,那么接口的抽象做用就受到了限制。若是没有了能够安全地预见到结果的抽象,开发人员就必须限制“组合爆炸”,这就限制了系统行为的丰富性。

所以:尽量把程序的逻辑放到函数(返回结果而不产生反作用的操做称为函数)中,由于函数是只返回结果而不产生明显反作用的操做。严格地把命令(引发明显的状态改变的方法)隔离到不返回领域信息的、很是简单的操做中。当发现了一个很是适合承担复杂逻辑职责的概念时,就能够把这个复杂逻辑移到VALUE OBJECT中,这样能够进一步控制反作用。


Paint分解了无反作用的Pigment Color类
 

模式:ASSERTION(断言)

把复杂的计算封装到SIDE-EFFECT-FREE FUNCTION中能够简化问题,但实体仍然会留有一些有反作用的命令,使用这些ENTITY的人必须了解使用这些命令的后果。

若是操做的反作用仅仅是由它们的实现隐式定义的,那么在一个具备大量相互调用关系的系统中,原由和结果会变得一团糟。理解程序的惟一方式就是沿着分支路径来跟踪程序的执行,封装彻底失去了价值。跟踪具体的执行也使抽象失去了意义。

所以:把操做的后置条件和类及AGGREGATE的固定规则表达清楚。若是在你的编程语言中不能直接编写ASSERTION,那么就把它们编写成自动的单元测试。还能够把它们写到文档或图中(若是符合项目开发风格的话)。寻找在概念上内聚的模型,以便使开发人员更容易推断出预期的ASSERTION,从而加快学习过程并避免代码矛盾。

INTENTION-REVEALING INTERFACE清楚地代表了用途,SIDE-EFFECT-FREE FUNCTION和ASSERTION使咱们可以更准确地预测结果,所以封装和抽象更加安全。

 

模式:CONCEPTUAL CONTOUR(概念轮廓)

若是把模型或设计的全部元素都放在一个总体的大结构中,那么它们的功能就会发生重复。外部接口没法给出客户可能关心的所有信息。因为不一样的概念被混合在一块儿,它们的意义变得很难理解。

而另外一方面,把类和方法分解开也多是毫无心义的,这会使客户更复杂,迫使客户对象去理解各个细微部分是如何组合在一块儿的。更糟的是,有的概念可能会彻底丢失。铀原子的一半并非铀。并且,粒度的大小并非惟一要考虑的问题,咱们还要考虑粒度是在哪一种场合下使用的。

所以:把设计元素(操做、接口、类和AGGREGATE)分解为内聚单元,在这个过程当中,你对领域中一切重要划分的直观认识也要考虑在内。在连续的重构过程当中观察发生变化和保证稳定的规律性,并寻找可以解释这些变化模式的底层CONCEPTUAL CONTOUR。使模型与领域中那些一致的方面(正是这些方面使得领域成为一个有用的知识体系)相匹配。

咱们的目标是获得一组能够在逻辑上组合起来的简单接口,使咱们能够用UBIQUITOUS LANGUAGE进行合理的表述,而且使那些无关的选项不会分散个人注意力,也不增长维护负担。但这一般是经过重构才能获得的结果,很难在前期就实现。并且若是仅仅是从技术角度进行重构,可能永远也不会出现这种结果;只有经过重构获得更深层次的理解,才能实现这样的目标。

INTENTION-REVEALING INTERFACE使客户可以把对象表示为有意义的单元,而不只仅是一些机制。SIDE-EFFECT-FREE FUNCTION和ASSERTION使咱们能够安全地使用这些单元,并对它们进行复杂的组合。CONCEPTUAL CONTOUR的出现使模型的各个部分变得更加稳定,也使得这些单元更直观,更易于使用和组合。

 

模式:STANDALONE CLASS(独立的类)

即便是在MODULE内部,设计也会随着依赖关系的增长而变得愈来愈难以理解。这加剧了咱们的思考负担,从而限制了开发人员能处理的设计复杂度。隐式概念比显式引用增长的负担更大了。

低耦合是对象设计的一个基本要素。尽一切可能保持低耦合。把其余全部无关概念提取到对象以外。这样类就变得彻底独立了,这就使得咱们能够单独地研究和理解它。每一个这样的独立类都极大地减轻了因理解MODULE而带来的负担。

尽力把最复杂的计算提取到STANDALONE CLASS(独立的类)中,实现此目的的一种方法是从存在大量依赖的类中将VALUE OBJECT建模出来。低耦合是减小概念过载的最基本方法。独立的类是低耦合的极致。

 

模式:CLOSURE OF OPERATION(闭合操做)

        两个实数相乘,结果仍为实数(实数是全部有理数和全部无理数的集合)。因为这一点永远成立,所以咱们说实数的“乘法运算是闭合的”:乘法运算的结果永远没法脱离实数这个集合。当咱们对集合中的任意两个元素组合时,结果仍在这个集合中,这就叫作闭合操做。

                         ——The Math Forum,Drexel University

加法运算是实数集中的闭合运算。数学家们都极力避免去引入无关的概念,而闭合运算的性质正好为他们提供了这样一种方式。

所以:在适当状况下,在定义操做时让它的返回类型与其参数的类型相同。若是实现者(implementer)的状态在计算中会被用到,那么实现者实际上就是操做一个参数,所以参数和返回值应该与实现者有相同的类型。这样的操做就是在该类型的实例集合中的闭合操做。闭合操做提供了一个高层接口,同时又不会引人对其余概念的任何依赖。

 

【学习心得】:经历了(还在经历)一个近十年的项目,我想本身仍是比较有资格谈谈柔性设计的感觉。没有学习这些柔性概念以前,咱们能持续高效并运行开发一个项目那么长时间,功劳归于一个重要的原则:简单。起初,整个团队都缺少以上这些实用的模式理论做为参考,但你们都有秉承着一个“简单”的共同原则,其实不知不觉中摸着石头过河,在无数次重构中逐渐跟以上模式契合起来。固然,若是咱们能提早认识这些基础的理论基础知识,我想没必要要的弯路会少走许多。也固然,系统还在不断完善中,如今认识也不晚。

 

第11章:应用分析模式

在《分析模式》一书中,Martin Fowler这样定义分析模式:

分析模式是一种概念集合,用来表示业务建模中的常见结构。它可能只与一个领域有关,也可能跨多个领域。

Fowler所提出的分析模式来自于实践经验,所以只要用在合适的情形下,它们会很是实用。对于那些面对着具备挑战性领域的人们,这些模型为他们的迭代开发过程提供了一个很是有价值的起点。“分析模式”这个名字自己就强调了其概念本质。分析模式不是技术方案,他们只是参考,用来指导人们设计特定领域中的模型。

分析模式最大的做用是借鉴其余项目的经验,把那些项目中有关设计方向和实现结构的普遍讨论与当前模型的理解结合起来。脱离具体的上下文来讨论模型思想不但难以落地,并且还会形成分析与设计严重脱节的风险,而这一点正是MODEL-DRIVEN DESIGN坚定反对的。

当你能够幸运地使用一种分析模式时,它通常并不会直接知足你的需求。但它为你的研究提供了有价值的线索,并且提供了明确抽象的词汇。它还能够知道咱们的实现,从而省去不少麻烦。

咱们应该把全部分析模式的知识融入知识消化和重构的过程当中,从而造成更深入的理解,并促进开发。当咱们应用一种分析模式时,所获得的结果一般与该模式的文献中记载的形式很是想像,只是因具体状况不一样而略有差别。但有时彻底看不出这个结果与分析模式自己有关,然而这个结果仍然是受该模式思想的启发而获得的。

但有一个误区是应该避免的。当使用众所周知的分析模式中的术语时,必定要注意,无论其表面形式的变化有多大,都不要改变它所表示的基本概念。这样作有两个缘由,一是模式中蕴含的基本概念将帮助咱们避免问题,二是(也是更重要的缘由)使用被普遍理解或至少是被明确理解的术语能够加强UBIQUITOUS LANGUAGE。若是在模型的天然演变过程当中模型的定义也发生改变,那么就要修改模型名称了。

 

【学习心得】:本章节主要仍是借助《分析模式》一书中的例子,用实践例子来分析系统是如何在演绎过程使用模型的。这种科学谨慎的作法,才是一个工程师的基本观念要求。

 

第12章:将设计模式应用于模型

在《设计模式》中,有些(但并不是全部)模式可用做领域模式,但在这样使用的时候,须要变换一下重点。有些模式反映了一些在领域中出现的深层概念。这些模式都有很大的利用价值。为了在领域驱动设计中充分利用这些模式,咱们必须同时从两个角度看待它们:从代码的角度来看它们是技术设计模式,从模型的角度来看它们就是概念模式。

模式:STRATEGY(也称POLICY)


策略模式

策略模式:定义了一组算法,将每一个算法封装起来,并使它们能够互换。STRATEGY容许算法独立于使用它的客户而变化[Gamma et al. 1995]

领域模型包含一些并不是用于解决技术问题的过程,将它们包含进来是由于它们处理问题领域具备实际的价值。当必须从多个过程当中进行选择时,选择的复杂性再加上多个过程自己的复杂性使局面失去控制。

所以:咱们须要把过程当中的易变部分提取到模型的一个单独的“策略”对象中。将规则与它所控制的行为区分开。按照STRATEGY设计模式来实现规则或可替换的过程。策略对象的多个版本表示了完成过程的不一样方式。

 

模式:COMPOSITE


组合模式

组合模式:将对象组织为树来表示部分—总体的层次结构。利用COMPOSITE,客户能够对单独的对象和对象组合进行一样的处理。[Gamma et al.1995]

当嵌套容器的关联性没有在模型中反映出来时,公共行为必然会在层次结构的每一层重复出现,并且嵌套也变得僵化(例如,容器一般不能包含同一层中的其余容器,并且嵌套的层数也是固定的)。客户必须经过不一样的接口来处理层次结构中的不一样层,尽管这些层在概念上可能没有区别。经过层次结构来递归地收集信息也变得很是复杂。

所以:定义一个把COMPOSITE的全部成员都包含在内的抽象类型。在容器上实现那些查询信息的方法时,这些方法返回由容器内容所汇总的信息。而“叶”节点则基于它们本身的值来实现这些方法。客户只需使用抽象类型,而无需区分“叶”和容器。

 

【学习心得】:不少时候,技术人员钉子思惟是没法区分技术角度和模型角度。虽然许多方法是相通的,但不通维度的思考方式也会产生巨大的效果。学以至用,不是停留在嘴巴上,是在实践中证实的。

 

第13章:经过重构获得更深层次的理解

经过重构获得更深层次的理解是一个涉及不少方面的过程。咱们有必要暂停一下,把一些要点概括到一块儿。有三件事情是必需要关注的:

(1)以领域为本;

(2)用一种不一样的方式来看待事物;

(3)始终坚持与领域专家对话。

在寻求理解领域的过程当中,能够发现更普遍的重构机会。但一提到传统意义上的重构,咱们头脑中就会出现这样一幅场景:一两位开发人员坐在键盘前面,发现一些代码能够改进,而后马上动手修改代码(固然还要用单元测试来验证结果)。这个过程应该是一直进行下去,但它并非重构过程的所有。

一、开始重构

与传统重构观点不一样的是,即便在代码看上去很整洁的时候也可能须要重构,缘由是模型的语言没有与领域专家保持一致,或者新需求不能被天然地添加到模型中。重构的缘由也可能来自学习:当开发人员经过学习得到了更深入的理解,从而发现了一个获得更清晰或更有用的模型的机会。

二、探索团队

无论问题的根源是什么,下一步都是要找到一种可以使模型表达变得更清楚和更天然的改进方案。这可能只须要作一些简单、明显的修改,只需几小时便可完成。在这种状况下,所作的修改相似于传统重构。但寻找新模型可能须要更多时间,并且须要更多人参与。

修改的发起者会挑选几位开发人员一块儿工做,这些开发人员应该擅长思考该类问题,了解领域,或者掌握深厚的建模技巧。若是涉及一些难以捉摸的问题,他们还要请一位领域专家加入。想要保证重构迭代过程的效率,须要注意几个关键事项:自主决定,注意范围和休息,以及练习使用UBIQUITOUS LANGUAGE。

三、借鉴先前的经验

咱们没有必要总去作一些无谓的重复工做。用于查找缺失概念或改进模型的头脑风暴过程具备巨大的做用,经过这个过程能够收集来自各个方面的想法,并把这些想法与已有知识结合起来。随着知识消化的不断开展,就能找到当前问题的答案。

四、针对开发人员的设计

软件不只仅是为用户提供的,也是为开发人员提供的。开发人员必须把他们编写的代码与系统的其余部分集成到一块儿。在迭代过程当中,开发人员反复修改代码。开发人员应该经过重构获得更深层的理解,这样既可以实现柔性设计,也可以从这样一个设计中获益。

五、重构的时机

若是一直等到彻底证实了修改的合理性以后才去修改,那么可能要等待太长时间了。项目正承受巨大的耗支,推迟修改将使修改变得更难执行,由于要修改的代码已经变得更加复杂,并更深地嵌入到其余代码中。持续重构渐渐被认为是一种“最佳实践”,但大不部分团队仍然对它抱有很大的戒心。

在探索领域的过程当中,在培训开发人员的过程当中,以及在开发人员与领域专家进行思想交流的过程当中,必须始终坚持把“经过重构获得更深层次理解”做为这些工做的一部分。所以,当发生一下状况时,就应该进行重构了:

□ 设计没有表达出团队对领域的最新理解;

□ 重要的概念被隐藏在设计中了(并且你已经发现了把它们呈现出来的方法);

□ 发现了一个能令某个重要的设计部分变得更灵活的机会。

六、危机就是机遇

传统意义上的重构听起来是一个很是稳定的过程。但经过重构获得更深层理解每每不是这样的。在对模型进行一段时间稳定的改进后,你可能忽然有所顿悟,而这会改变模型中的一切。这些突破不会天天都发生,然而很大一部分深层模型和柔性设计都来自这些突破。

这样的状况每每看起来不像是机遇,而更像危机。例如,你忽然发现模型中一些明显的缺陷,在表达方面显示出一个很大的漏洞,或存在一些没有表达清楚的关键区域。或者有些描述是彻底错误的。这些都代表团队对模型的理解已经达到了一个新的水平。他们如今站在更高的层次上发现了原有模型的弱点。他们能够从这种角度构思一个更好的模型。

 

【学习心得】:我曾几什么时候一直认为,发现本身问题是一种耻辱。这种思惟极其可怕,当我再也不发现本身问题的时候,那才叫可怕。在软件领域,新思惟的提高叫重构,在生活方面,新观念的造成叫重生。

 

【第四部分】战略设计


第14章:保持模型的完整性

模型最基本的要求是它应该保持内部一致,术语总具备相同的意义,而且不包含相互矛盾的规则:虽然咱们不多明确地考虑这些要求。模型的内部一致性又叫统一(unification),这种状况下,每一个术语都不会有模棱两可的意义,也不会有规则冲突。除非模型在逻辑上是一致的,不然它就没有意义。在理想世界中,咱们能够获得涵盖整个企业领域的单一模型。这个模型将是统一的,没有任何相互矛盾或相互重叠的术语定义。每一个有关领域的逻辑声明都是一致的。

但,大型系统开发并不是如此理想。在整个企业系统中保持这种水平的统一是一件得不偿失的事情。在系统的各个不一样部分中开发多个模型是颇有必要的,但咱们必须慎重地选择系统的哪些部分能够分开,以及它们之间是什么关系。咱们须要用一些方法保持模型关键部分的高度统一。全部这些都不会自行发生,并且光有良好的意愿也没用的。它只有经过有意识的设计决策和创建特定过程才能实现。大型系统领域模型的彻底统一既不可行,也不划算。

 

模式:BOUNDED CONTEXT(边界上下文)

细胞之因此可以存在,是由于细胞膜限定了什么在细胞内,什么在细胞外,而且肯定了什么物质能够经过细胞膜。

任何大型项目都会存在多个模型。而当基于不一样模型的代码被组合到一块儿后,软件就会出现bug,变得不可靠和难以理解。团队成员之间的沟通变得混乱。人们每每弄不清楚一个模型不该该在哪一个上下文中使用。

所以:明确地定义模型所应用的上下文。根据团队的组织,软件系统的各个部分的用法以及物理表现(代码和数据库模式等)来设置模型的边界。在这些边界中严格保持模型的一致性,而不要收到边界以外问题的干扰和混淆。

但记住,BUOUNDED CONTEXT不是MODULE。有时这两个概念易引发混淆,但它们是具备不一样动机的不一样模式。确实,当两组对象组成两个不一样模型时,人们几乎老是把它们放在不一样的MODULE中。这样作的确提供了不一样的命名空间(对不一样的CONTEXT很重要)和一些划分方法。但人们也会在一个模型中用MODULE来组织元素,它们不必定要表达划分CONTEXT的意图。MODULE在BOUNDED CONTEXT内部建立的独立命名空间实际上令人们很难发现意外产生的模型分裂。

咱们经过定义这个BOUNDED CONTEXT,最终获得了什么?对CONTEXT内的团队而言:清晰!对于CONTEXT以外的团队而言:自由。固然,边界只不过是一些特殊的位置。各个BUONDED CONTEXT之间的关系须要咱们仔细地处理。CONTEXTMAP(上下文图)画出了上下文范围,并给出了CONTEXT以及它们之间联系的整体视图,而几种模式定义了CONTEXT之间的各类关系的性质。CONTINUOUS INTEGRATION(持续集成)的过程能够使模型在BOUNDED CONTEXT中保持一致。

如何识别BOUNDED CONTEXT中的不一致?不少征兆均可能代表模型出现了差别。最明显的是已编码的接口不匹配。对于更微妙的状况,一些意外行为也多是一种信号。采用了自动测试的CONTINUOUS INTEGRATION能够帮助捕捉到这类问题,但语言上的混乱每每是一种早期信号。

将不一样模型的元素组合到一块儿可能会引起两类问题:重复的概念和假同源。重复的概念是指两个模型元素(以及伴随的实现)实际上表示同一个概念,而假同源是指使用相同术语(或已实现的对象)的两我的认为他们是在谈论同一件事情,但实际上并非这样。假同源可能稍微少见一点,但它潜在的危害更大。

 

模式:CONTINUOUS INTEGRATION(持续集成)

当不少人在同一个BOUNDED CONTEXT中工做时,模型很容易发生分裂。团队越大,问题就越大,但即便三、4我的的团队也有可能会遇到严重的问题。然而,若是将系统分解为更小的CONTEXT,最终又难以保持集成度和一致性。

所以:创建一个把全部代码和其余实现工件频繁地合并到一块儿的过程,并经过自动化测试来快速查明模型的分类问题。严格坚持使用UBIQUITOUS LANGUAGE,以便在不一样人的头脑中演变出不一样的概念时,是全部人对模型都能达成一个共识。

 

模式:CONTEXT MAP(上下文总体关联图)

其余团队中的人员并非十分清楚CONTEXT的边界,他们会不知不觉地作出一些更改,从而使边界变得模糊或者使互连变得复杂。当不一样的上下文必须互相链接时,它们可能会互相重叠。

所以:识别在项目中起做用的每一个模型,并定义其BOUNDED CONTEXT。这包括非面向对象子系统的隐含模型。为每一个BOUNDED CONTEXT命名,并把名称添加到UBIQUITOUS LANGUAGE中。描述模型之间的联系点,明确全部通讯须要的转换,并突出任何共享的内容。先将当前的状况描绘出来,之后再作改变。

CONTEXT MAP无需拘泥于任何特定的文档格式。我发现相似本章的简图在可视化和沟通上下文图方面颇有帮助。有些人可能喜欢使用较多的文本描述或别的图形表示。在某些状况下,团队成员之间的讨论就足够了。需求不一样,细节层次也不一样。无论CONTEXT MAP采用什么形式,它必须在全部项目人员之间共享,并被他们理解。它必须为每一个BOUNDED CONTEXT提供一个明确的名称,并且必须阐明联系点和它们的本质。

下面介绍的这些模式涵盖了将两个模型关联起来的众多策略。这些模式的主要区别包括你对另外一个模型的控制程度、两个团队以前合做水平和合做类型,以及特性和数据的集成程度。

 

模式:SHARED KERNEL(共享内核)

当不一样团队开发一些紧密相关的应用程序时,若是团队之间不进行协调,即便短期内可以得到快速进展,但他们开发出的产品可能没法结合到一块儿。租后可能不得不耗费大量精力在转换层上,而且频繁地进行改动,不如一开始就用CONTINUOUS INTEGRATION那么省心省力,同时这也形成重复工做,而且没法实现公共的UBIQUITOUS LANGUAGE所带来的好处。

所以:从模型中选出两个团队都赞成共享的一个子集。固然,除了这个模型子集之外,还包括与该模型部分相关的代码子集,或数据库设计的子集。这部分明确共享的内容具备特殊的地位,一个团队在没与另外一个团队商量的状况下不该擅自更改它。功能系统要常常进行集成,但集成的频率应该比团队中CONTINUOUS INTEGRATION的频率低一些。在进行这些集成的时候,两个团队都要运行测试。

 

模式:CUSTOMER/SUPPLIER DEVELOPMENT TEAM(客户/供应商 开发团队)

咱们常常会碰到这样的状况:一个子系统主要服务于另一个子系统;“下游”组件执行分析或其余功能,这些功能向“上游”组件反馈的信息很是少,全部依赖都是单向的。两个子系统一般服务于彻底不一样的用户群,其执行的任务也不一样,在这种状况下使用不一样的模型会颇有帮助。

若是下游团队对变动具备否决权,或请求变动的程序太复杂,那么上游团队的开发自由度就会受到限制。因为担忧破坏下游系统,上游团队甚至会受到抑制。同时,因为上游团队掌握优先权。下游团队有时也会无能为力。

所以:在两个团队之间创建一种明确的客户/供应商关系。在计划会议中,下游团队至关于上游团队的客户。根据下游团队的需求来协商须要执行的任务并为这些任务作预算,以便每一个人都知道双方的约定和进度。两个团队共同开发自动化验收测试,用来验证预期的接口。把这些测试添加到上游团队的测试套件中,以便做为其持续集成的一部分来运行。这些测试使上游团队在作出修改时没必要担忧对下游团队产生反作用。

 

模式:CONFORMIST(承诺)

当两个具备上游/下游关系的团队不归同一个管理者指挥时,CUSTOMER/SUPPLIER TEAM这样的合做模式就不会凑效。勉强应用这种模式会给下游团队带来麻烦。

当两个开发团队具备上/下游关系时,若是上游团队没有动力来知足下游团队的需求,那么下游团队将无能为力。出于利他主义的考虑,上游开发人员可能会作出承诺,但他们可能不会履行承诺。下游团队出于良好的意愿会相信这些承诺,从而根据一些永远不会实现的特性来制定计划。下游项目只能被搁置,直到团队最终学会利用现有条件自力更生为止。下游团队不会获得根据他们的需求而量身定作的接口。

所以:经过严格听从上游团队的模型,能够消除在BOUNDED CONTEXT之间进行转换的复杂性。尽管这会限制下游设计人员的风格,并且可能不会获得理想的应用程序模型,但选择CONFORMITY模式能够极大地简化集成。此外,这样还能够与供应商团队共享UBIQUITOUS LANGUAGE。供应商处于统治地位,所以最好使沟通变容易。他们从利他主义的角度出发,会与你分享信息。

SHARED KERNEL是两个高度协调的团队之间的合做模式,而CONFORMIST模式则是应对一个对合做不感兴趣的团队进行集成。

 

模式:ANTICORRUPTION LAYER(隔离层)

新系统几乎老是须要与遗留系统或其余系统进行集成,这些系统具备本身的模型。当把参与集成的BOUNDED CONTEXT设计完善而且团队相互合做时,转换层可能很简单,甚至很优雅。可是,当边界那侧发生渗透时,转换层就要承担起更多的防御职责。

当正在构建的新系统与另外一个系统的接口很大时,为了克服链接两个模型而带来的困难,新模型所表达的意图可能会被彻底改变,最终致使它被修改得像另外一个系统的模型了(以一种特定风格)。遗留系统的模型一般很弱。即便对于那些模型开发得很好的例外状况,它们可能也不符合当前项目的须要。然而,集成遗留系统仍然具备很大的价值,并且有时仍是绝对必要的。

所以:建立一个隔离层,以便根据客户本身的领域模型来为客户提供相关功能。这个层经过另外一个系统现有接口与其进行对话,而只需对那个系统做出不多的修改,甚至无需修改。在内部,这个层在两个模型之间进行必要的双向转换。

这种链接两个系统的机制可能会使咱们想到把数据从一个程序传输到另外一个程序,或者从一个服务器传输到另外一个服务器。咱们很快就会讨论技术通讯机制的使用。但这些细节问题不该与ANTICORRUPTION LAYER混淆,由于ANTICORRUPTION LAYER并非向另外一个系统发送消息的机制。想反,它是不一样的模型和协议之间转换概念对象和操做的机制。

如何设计ANTICORRUPTION LAYER的接口?

ANTICORRUPTION LAYER的公共接口一般以一组SERVICE形式出现,但偶尔也会采用ENTITY的形式。在咱们的模型中,把外部系统表示为一个单独组件多是没有意义的。最好是使用多个SERVICE(或偶尔使用ENTITY),其中每一个SERVICE都使用咱们的模型来履行一致的职责。

如何实现ANTICORRUPTION LAYER?

对ANTICORRUPTION LAYER设计进行组织的一种方法是把它实现为FACEDE、ADAPTER和转换器的组合,外加两个系统之间进行对话所需的通讯和传输机制。


ANTICORRUPTION LAYER的结构
 

模式:SEPARATE WAY(各行其道)

咱们必须严格划定需求的范围。若是两组功能之间的关系并不是必不可少,那么两者彻底能够彼此独立。由于集成老是代价高昂,而有时获益却很小。

所以:声明一个与其余上下文毫无关联的BOUNDED CONTEXT,使开发人员可以在这个小范围内找到简单、专用的解决方案。

采用SEPARATE WAY(各行其道)模式须要预先决定一些选项。尽管持续重构最后能够撤销任何决策,但彻底隔离开发的模型是很难合并的。若是最终仍须要集成,那么转换层将是必要的,并且可能很复杂。固然,无论怎样,这都是咱们将要面对的问题。如今,让咱们回到更为合做的关系上,来看一下几种提升集成度的模式。

 

模式:OPEN HOST SERVICE(开放主机服务)

当一个子系统必须与大量其余系统进行集成时,为每一个集成都定制一个转换层可能会减慢团队的工做速度。须要维护的东西会愈来愈多。并且进行修改的时候担忧的事情也会愈来愈多。

所以:定义一个协议,把你的子系统做为一组SERVICE供其余系统访问。开放这个协议,以便全部须要与你的子系统集成的人均可以使用它。当有新的集成需求时,就加强并扩展这个协议,但个别团队的特殊需求除外。知足这种特殊需求的方法是使用一次性的转换器来扩充协议,以便使共享协议简单且内聚。

这种通讯形式暗含一些共享的模型词汇,它们是SERVICE接口的基础。这样,其余子系统就变成了与OPEN HOST(开放主机)的模型相链接,而其余团队则必须学习HOST团队所使用的专用术语。在一些状况下,使用一个众所周知的PUBLISHED LANGUAGE(公开发布的语言)做为交换模型能够减小耦合并简化理解。

 

模式:PUBLISHED LANGUAGE(公开发布的语言)

与现有领域模型进行直接的转换可能不是一种好的解决方案。这些模型可能过于复杂或设计得较差。它们可能没有被很好地文档化。若是把其中一个模型做为数据交互语言,它实际上就被固定住了,而没法知足新的开发需求。

所以:把一个良好文档化的、可以表达出所需领域信息的共享语言做为公共的通讯媒介,必要时在其余信息与该语言之间进行转换。

 

“大象”的统一

讲一个盲人摸象的故事:

第一个盲人碰巧摸到了大象宽阔结实的身躯,就觉得大象就像一堵墙;

······

第三个盲人碰巧把扭动着的象鼻抓在书中,就大胆认为大象就像一条蛇;

第四个盲人急切伸出双手,摸到了大象的膝盖,就很明显地认为大象就像一颗树;

······

第六个盲人一开始摸这头大象,就抓住了它摆动着的尾巴,就认为大象就像一根绳子。

即使他们对大象的本质不能达成完成的一致,这些盲人仍然能够根据他们所触摸到的大象身体的部位来扩展各自的认识。若是并不须要集成,那么模型统不统一就可有可无。若是他们须要进行一些集成,那么实际上并不须要对大象是什么达成一致,而只要接受各类不一样意见就会得到不少价值。这样,他们就会在不知不觉中互不相让。


4个没有集成的上下文

当盲人想要分享更多大象的信息时,他们会共享单个BOUNDED CONTEXT获得更大的价值。但统一多个模型几乎老是意味着建立一个新模型:


4个只有最小集成的上下文

通过一些想象和讨论(也许是激烈的讨论)以后,盲人们最终可能会认识到他们正在对一个更大总体的不一样部分进行描述和建模。从不少方面来说,部分-总体的统一可能不须要花费不少工做。至少集成的第一步只需弄清楚各个部分是如何相连的就够了。


一个粗略集成的上下文

尽管咱们已经把部分合并成一个总体,但获得的模型仍是很简陋的。他缺少内聚性,也没有造成任何潜在的领域的轮廓。在持续精华的过程当中,新的理解可能会产生更深层的模型。


一个更深刻集成的上下文

认可多个相互冲突的领域模型实际上正式面对现实的作法。经过明肯定义每一个模型都适用的上下文,能够维护每一个模型的完整性,并清楚地看到要在两个模型之间建立的任何特殊接口的含义。盲人没办法看到整个大象,但只要他们认可各自的理解是不完整的,他们的问题就能获得解决。

 

选择你的模型上下文策略

在任什么时候候,绘制出CONTEXT MAP来反映当前情况都是很重要的。可是,一旦绘制好CONTEXT MAP以后,你极可能想要改变现状。现状,你能够开始有意识地选择CONTEXT的边界和关系。如下是一些指导原则:

一、团队决策或更高层决策

按照自己价值来讲,在决定是否扩展或分割BOUNDED CONTEXT时,应该权衡团队独立工做的价值以及能产生直接且丰富集成的价值,以这两种价值的成本-效益做为决策的依据。

在实践中,团队之间的行政关系每每决定了系统的集成方式。因为汇报结构,有技术优点的统一可能没法实现。管理层所要求的合并可能并不实用。你不会总能获得你想要的东西,大你至少能够评估出这些决策的代价,并反映给管理层,以便采起相应的措施来减小代价。

二、置身上下文中

开发软件项目时,咱们首先是对本身团队正在开发的那些部分感兴趣(“设计中的系统”),其次是对那些与咱们交互的系统感兴趣。这是一种简单、典型的状况,能让你对可能遇到的情形有一些粗略的了解。

实际上,咱们正式本身所处理的主要CONTEXT的一部分,这会在咱们的CONTEXT MAP中反映出来。只要咱们知道本身存在偏好,而且超过该CONTEXT MAP的应用边界时可以意识到已越界,那么就不会有什么问题。

三、转换边界

在画出BOUNDED CONTEXT的边界时,有无数种状况,也有无数种选择。但权衡时所要考虑的一般是下面所列出的某些因素。

首选较大的BOUNDED CONTEXT:

□ 当用一个统一模型来处理更多任务时,用户任务之间的流动更顺畅。

□ 一个内聚模型比两个不一样模型再加它们之间的映射更容易理解。

□ 两个模型之间的转换可能会很难(有时甚至是不可能的)。

□ 共享语言能够使团队沟通起来更清楚。

首选较小的BOUNDED CONTEXT:

□ 开发人员之间的沟通开销减小了。

□ 因为团队和代码规模较小,CONTINOUS INTEGRATION更容易了。

□ 较大的上下文要求更加通用的抽象模型,而掌握所需技巧的人员会出现短缺。

□ 不一样模型能够知足一些特殊需求,或者是可以把一些特殊用户群的专门术语和UBIQUITOUS LANGUAGE的专门术语包括进来。

四、接受那些咱们没法更改的事物:描述外部系统

最好从一些简单的决策开始。一些子系统显然不在开发中的系统的任何BOUNDED CONTEXT中。一些没法当即淘汰的大型遗留系统和那些提供所需服务的外部系统就是这样的例子。咱们很容易就能识别出这些系统,并把它们与你的设计隔离开。

在作出假设时必需要保持谨慎。咱们会轻易地认为这些系统构成了其本身的BOUNDED CONTEXT,但大多数外部系统只是勉强知足定义。

五、与外部系统的关系

这里能够应用3种模式。首先,能够考虑SEPARATE WAY模式。固然,若是你不须要集成,就不用把它们包括进来。但必定要真正肯定不须要集成。只为用户提供对两个系统的简单访问确实够用吗?集成要花费很大的代价并且还会分散精力,所以要尽量为你的项目减轻负担。

若是集成确实很是重要,能够在两种极端的模式之中选择:CONFORMIST模式或ANTICORRUPTION LAYER模式。

六、设计中的系统

你的项目团队正在构建的软件就是设计中的系统。你能够在这个区域内声明BOUNDED CONTEXT,并在每一个BOUNDED CONTEXT中应用CONTINOUS INTEGRATION,以便保持它们的统一。但应该有几个上下文呢?各个上下文之间又应该是什么关系呢?

状况可能很是简单:设计中的整个系统使用一个BOUNDED CONTEXT。例如,当一个少于10我的的团队正在开发高度相关的功能时,这可能就是一个很好的选择。

随着团队规模的增大,CONTINOUS INTEGRATION可能会变得困难起来(尽管我也曾看过一些较大的团队仍能保持持续集成)。你可能但愿采用SHARED KERNEL模式,并把几组相对独立的功能划分到不一样的BOUNDED CONTEXT中,使得在每一个BOUNDED CONTEXT中工做的人员少于10人。在这些BOUNDED CONTEXT中,若是有两个上下文之间的全部依赖都是单向的,就能够建成CUSTOMER/SUPPLIER DEVELOPMENT TEAM。

你可能认识到两个团队的思想大相径庭,以至他们的建模工做老是发生矛盾。若是这种矛盾的缘由是你没法改变或不想改变的,那么可让他们的模型采用SEPARATE WAY模式。在须要集成的地方,两个团队能够共同开发维护一个转换层,把它做为惟一的CONTINOUS INTEGRATION点。这与同外部系统的集成正好相反,在外部集成中,通常由ANTICORRUPTION LAYER来起调节做用,并且从另外一端得不到太多的支持。

通常来讲,每一个BOUNDED CONTEXT对应一个团队。一个团队也能够维护多个BOUNDED CONTEXT,但多个团队在一个上下文中工做倒是比较难的。

七、用不一样模型知足特殊需求

你可能决定经过不一样的BOUNDED CONTEXT来知足这些特殊需求,除了转换层的CONTINOUS INTEGTATION之外,让模型采用SEPARATE WAY模式。UBIQUITOUS LANGUAGE的不一样专用术语将围绕这些模型以及它们所基于的行话来发展。若是两种专用术语有不少重叠之处,那么SHARED KERNEL模式就能够知足特殊化要求,同时又能把转换成本减至最小。

最重要的是:这个用户群的专门术语有多大的价值?你必须在团队独立操做的价值与转换的风险之间作出权衡,而且留心合理地处理一些没有价值的术语变化。但记住,在须要大量集成的地方,转换成本会大大增长。

八、部署

在复杂系统中,对打包和部署进行协调是一项繁琐的任务,这类任务老是要比看上去可贵多。BOUNDED CONTEXT策略的选择将影响部署。因为部署环境和技术存在不一样,有不少技术因素须要考虑。但BOUNDED CONTEXT关系能够为咱们指出重点问题。转换接口已经被标出。因此,绘制CONTEXT边界时应该反映出部署计划的可行性。

九、权衡

经过总结这些知道原则可知有不少统一或集成模型的策略。通常来讲,咱们须要在无缝功能集成的益处和额外的协调和沟通工做之间作出权衡。


CONTEXT关系模型的相对要求

十、当项目正在进行时

不少状况下,咱们不是从头开发一个项目,而是会改进一个正在开发的项目。在这种状况下,第一步是根据当前的情况来定义BOUNDED CONTEXT。这很关键。为了有效地定义上下文,CONTEXT MAP必须反映出团队的实际工做,而不是反映那个经过遵照以上描述的指导原则而得出的理想组织。

描述了当前真实的BOUNDED CONTEXT以及它们的关系之后,下一步就是围绕当前组织结构来增强团队的工做。在CONTEXT中增强CONTINOUS INTEGRATION。把全部分散的转换代码重构到ANTICORRUPTION LAYER中。命名现有的BOUNDED CONTEXT,并确保它们处于项目的UBIQUITOUS LANGUAGE中。

下一节将讨论如何修改CONTEXT边界:转换。

 

转换

像建模和设计的其余方面,有关BOUNDED CONTEXT的决策并不是不可改变的。在不少状况下,咱们必须改变最初有关边界以及BOUNDED CONTEXT之间关系的决策,这是不可避免的。通常而言,分割CONTEXT是很容易,但合并它们或改变它们之间的关系却很难。下面将介绍几种有表明性的修改,它们很难,但也很重要。

一、合并CONTEXT:SEPARETE WAY→SHARED KERNEL

合并BOUNDED CONTEXT的动机不少:翻译开销够高、重复现象很明显。合并很难,但何时作都不晚,只是须要一些耐心。

即便你的最终目标是彻底合并一个采用CONTINUOS INTEGRATION的CONTEXT,也应该先过渡到SHARED KERNEL。

(1)评估现状。在开始统一两个CONTEXT以前,必定要确信它们确实须要统一。

(2)创建合并过程。你须要决定代码的共享方式以及模块应该采用哪一种命名约定。SHARED KERNEL的代码至少每周要集成一次,并且它必须有一个测试套件。在开发任何共享代码以前,先把它设置好。(测试套件将是空的,所以很容易经过!)

(3)选择某个小的子领域做为开始,它应该是两个CONTEXT中重复出现的子领域,但不是CORE DOMAIN的一部分。

(4)从两个团队中共选出2~4位开发人员组成一个小组,有他们来为子领域开发一个共享的模型。

(5)来自两个团队的开发成员一块儿负责实现模型(或修改要共享的现有代码)、肯定各类细节并使模型开始工做。若是这些开发人员在模型中遇到了问题,就从第(3)步开始从新组织团队,并进行必要的概念修订工做。

(6)每一个团队的开发人员都承担与新的SHARED KERNEL集成的任务。

(7)清除那些再也不须要的翻译。

二、合并CONTEXT:SHARED KERNEL→CONTINOUS INTEGRATION

若是你的KERNEL正在扩大,你可能会被彻底统一两个BOUNDED CONTEXT的优势所吸引。但这并不仅是一个解决模型差别的问题。你将改变团队的结构,并且最终会改变人们所使用的语言。这个过程从人员和团队开始准备。

(1)确保每一个团队都已经创建了CONTINOUS INTEGRATION所需的全部过程(共享代码全部权、频繁集成等)。两个团队协商集成步骤,以便全部人都以同一步调工做。

(2)团队成员在团队之间流动。这样能够造成一大批同时理解两个模型的人员,而且能够把两个团队的人员联系起来。

(3)澄清每一个模型的精髓。

(4)如今,团队应该有了足够的信心把核心领域合并到SHARED KERNEL中。

(5)随着SHARED KERNEL的增加,把集成频率提升到天天一次,最后实现CONTINOUS INTEGRATION。

(6)当SHARED KERNEL逐渐把先前两个BOUNDED CONTEXT的全部内容都包括进来的时候,你会发现要么造成了一个大的团队,要么造成了两个较小的团队,这两个较小的团队共享一个CONTINOUS INTEGRATION的代码库,并且团队成员能够常常在两个团队之间来回流动。

三、逐步淘汰遗留系统

好花美丽不常开,好景怡人不常在,就算遗留计算机软件也同样会走向终结。但这可不会自动自发地出现。这些老的系统可能与业务及其余系统紧密交织在一块儿,所以淘汰它们可能须要不少年。好在咱们并不须要一次就把全部东西都淘汰掉。

首先要执行的步骤是肯定测试策略。应该为新系统中的新功能编写自动的单元测试,但逐步淘汰遗留系统还有一些特殊的测试要求。一些组织会在某段时间内同时运行新旧两个系统。在任何一次迭代中:

(1)肯定遗留系统的哪一个功能能够在一个迭代中被添加到某个新系统中;

(2)肯定须要在ANTICORRUPTION LAYER中添加功能;

(3)实现;

(4)部署;

(5)找出ANTICORRUPTION LAYER中那些没必要要的部分,并去掉它们;

(6)考虑删除遗留系统中目前未被使用的模块,虽然这种作法未必实际。

不断重复这几个步骤。遗留系统应该愈来愈少地参与业务,最终,替换工做会看到但愿的曙光并彻底中止遗留系统。

四、OPEN HOST SERVICE→PUBLISHED LANGUAGE

咱们已经经过一系列特意的协议与其余系统进行了集成,但随着须要访问的系统逐渐增多,维护负担也不断增长,或者交互变得很难理解。咱们须要经过PUBLISHED LANGUAGE来规范系统之间的关系。

(1)若是有一种行业标准语言可用,则尽量评估并使用它。

(2)若是没有标准语言或预先公开发布的语言,则完善做为HOST的系统的CORE DOMAIN。

(3)使用CORE DOMAIN做为交换语言的基础,尽量使用像XML这样的标准交互范式。

(4)(至少)向全部参与协做的各方发布语言。

(5)若是涉及新的系统架构,那么也要发布它。

(6)为每一个协做系统构建转换层。

(7)切换。

如今,当加入更多协做系统时,对整个系统的破坏已经减至最小了。

 

【学习心得】:学以至用,具体问题具体分析。模式毕竟是巨人的肩膀,要学会站着巨人肩膀看事情,不管项目多大仍是多下,又或者团队多大仍是多小,总有属于当前本身的模式。结合自身状况,找准定位。咱们所作的大部分事情几乎都有方法或模式借鉴,千万不要埋头单干。就像耗子叔所说,学会Evidence Driven:任何讨论和分析都要基于权威的证据、数据或是引用。在咱们作设计的时候,或是有争论的时候,说服对方最好的方式就是拿出证据、数据或是权威引用。好比:个人XX设计参考了TCP协议中的XX设计,个人XX观点是基于XX开源软件的实现……若是争论不休就中止争论,而后各自收集和调查本身观点的佐证。

 

第15章:精炼

如何才能专一于核心问题而不被大量的次要问题淹没呢?LAYERED ARCHITECTURE能够把领域概念从技术逻辑中(技术逻辑确保了计算机系统可以运转)分离出来,但在大型系统中,即便领域被分离出来,它的复杂性也可能仍然难以管理。

精炼是把一堆混杂在一块儿的组件分开的过程,以便经过某种形式从中提取出最重要的内容,而这种形式将使它更有价值,也更有用。模型就是知识的精炼。经过每次重构所获得的更深层的理解,咱们得以把关键的领域知识和优先级提取出来。

本章将展现对CORE DOMAIN进行战略精炼的系统性方法,解释如何在团队中有效地统一认识,并提供一种用于讨论工做的语言。


战略精炼的导航图
 

模式:CORE DOMAIN(核心领域)

在设计大型系统时,有很是多的组成部分——它们都很复杂并且对开发的功能也相当重要,到致使真正的业务资产——领域模型最为精华的部分——被掩盖和忽略了。

一个严峻的现实是咱们不可能对全部设计部分进行同等的精化,而是必须分出优先级。为了使领域模型成为有价值的资产,必须整齐地梳理出模型的真正核心,并彻底根据这个核心来建立应用程序的功能。但原本就稀缺的高水平开发人员每每会把工做重点放在技术基础设施上,或者只是去解决那些不须要专门领域知识就能理解的领域问题(这些问题都已经有了很好的定义)。

所以:对模型进行提炼。找到CORE DOMAIN并提供一种易于区分的方法把它与那些去辅助做用的模型和代码分开。最有价值和最专业的概念要轮廓分明。尽可能压缩CORE DOMAIN。让最有才能的人来开发CORE DOMAIN,并据此要求进行相应的招聘。在CORE DOMAIN中努力开发可以确保现实系统蓝图的深层模型和柔性设计。仔细判断任何其余部分的投入,看它是否可以支持这个提炼出来的CORE。

一、选择核心

咱们须要关注的是那些可以表示业务领域并解决业务问题的模型部分。一个应用程序中的CORE DOMAIN在另外一个应用程序中可能只是通用的支持组件。尽管如此,仍然能够在一个项目中(并且一般在一个公司中)定义一个一致的CORE。像其余设计部分同样,人们对CORE DOMAIN的认识也会随着迭代而发展。开始时,一些特殊关系可能显得不重要。而最初被认为是核心对象可能逐渐被证实只是起支持做用。

二、工做的分配

在项目团队中,技术能力最强的人员每每缺少丰富的领域知识。这限制了他们的做用,而且更倾向于分派他们来开发一些支持组件,从而造成了一个恶性循环——知识的缺少使他们远离了那些可以学到领域知识的工做。

打破这种恶心循环是很重要的,方法是创建一支由开发人员和一位或多位领域专家组成的联合团队,其中开发人员必须能力很强、可以长期稳定地工做而且学习领域知识很是感兴趣,而领域专家则要掌握深厚的业务知识。若是你认真对待领域设计,那么它就是一项有趣且充满技术挑战的工做。

三、精炼的逐步提高

本章接下来将要介绍各类精炼技术,它们在使用顺序上基本没什么要求,但对设计的改动却大不相同。请往下看:

 

模式:GENERIC SUBDOMIAN(通用子领域)

模型中有些部分除了增长复杂性之外并无捕捉或传递任何专门的知识。任何外来因素都会是CORE DOMAIN愈发的难以分辨和理解。模型中充斥着大量众所周知的通常原则,或者专门的细节,这些细节并非咱们的主要关注点,而只是起到支持做用。然而,不管它们是多么通用的元素,它们对实现系统功能和充分表达模型都是极为重要的。

所以:识别出那些与项目意图无关的内聚子领域。把这些子领域的通用模型提取出来,并放到单独的MODULE中。任何专有的东西都不该该放在这些模块中。把它们分离出来之后,在继续开发的过程当中,它们的优先级应低于CORE DOMAIN的优先级,而且不要分派核心开发人员来完成这些任务(由于他们不多可以从这些任务中得到领域知识)。此外,还能够考虑为这些GENERIC SUBDOMAIN使用现成的解决方案或“公开发布的模型”(PUBLISHED MODEL)。

当开发这样的软件包时,有如下几种选择:

一、现成的解决方案

二、公开发布的设计或模型

三、把实现外包出去

四、内部实现

 

模式:DOMAIN VISION STATEMENT(领域愿景说明)

在项目开始时,模型一般并不存在,可是模型开发的需求是早就肯定下来的重点。在后面的开发阶段,咱们须要解释清楚系统的价值,但这并不须要深刻地分析模型。此外,领域模型的关键方面可能跨越多个BOUNDED CONTEXT,并且从定义上看,没法将这些彼此不一样的模型组织起来代表其共同的关注点。

所以:写一份CORE DOMAIN的简短描述(大约一页纸)以及它将会创造的价值,也就是“价值主张”。那些不能将你的领域模型与其余领域模型区分开的方面就不要写了。展现出领域模型是如何实现和均衡各方面利益的。这份描述要尽可能精简。尽早把它写出来,随着新的理解随时修改它。

DOMAIN VISION STATEMENT能够用做一个指南,它帮助开发团队在精炼模型和代码的过程当中保持统一的方向。团队中的非技术成员,管理层甚至是客户也均可以共享领域愿景说明。

 

模式:HIGHLIGHTED CORE(突出核心)

DOMAIN VISION STATEMENT从宽泛的角度对CORE DOMAIN进行了说明,但它把什么是具体核心模型元素留给人们本身去解释和猜想。除

非团队的沟通极其充分,不然单靠VISION STATEMENT是很难产生什么效果的。

尽管团队成员可能大致上知道核心领域是由什么构成的,但CORE DOMIAN中到底包含哪些元素,不一样的人会有不一样的理解,甚至同一我的在不一样的时间也有会不一样的理解。若是咱们老是要不断过滤模型以便识别出关键部分,那么就会分散本应该投入到设计上的精力,并且这还须要普遍的模型知识。所以,CORE DOMAIN必需要很容易被分辨出来。

对代码所作的重大结构性改动是识别CORE DOMAIN的理想方式,但这些改动每每没法在短时间内完成。事实上,若是团队的认识还不够全面,这样的重大代码修改是很难进行的。

经过修改模型的组织结构(如划分GENERIC SUBDOMIAN和本章后面要介绍的一些改动),能够用MODULE表达出核心领域。但若是把它做为表达CORE DOMAIN的惟一方法,那么对模型的改动会很大,所以很难立刻看到结果。

咱们可能须要用一种轻量级的解决方案来补充这些激进的技术手段。可能有一些约束使你没法从物理上分离出CORE,或者你多是从已有代码开始工做的,而这些代码并无很好地区分出CORE,但你确实很须要知道什么是CORE并创建共识,以便有效地经过重构进行更好的精炼。咱们能够经过如下两种典型的表明性技术来突出核心:

一、精炼文档

编写一个很是简短的文档(3~7页,每页内容没必要太多),用于描述CORE DOMAIN以及CORE元素之间的主要交互过程。但独立文档带来的全部常见风险也会在这里出现(以下所示),控制这些风险的最好方法是保持绝对的精简。

(1)文档可能得不到维护;

(2)文档可能没人阅读;

(3)因为多个信息来源,文档可能达不到简化复杂性的目的。

二、标明CORE

可能你会遇到一份数百页的“领域模型”文档等资料,但无需慌张。把模型的主要存储库中的CORE DOMAIN标记出来,不用特地去阐明其角色。是开发人员很容易就知道什么在核心内,什么在核心外。只需作不多的处理和维护工做,便可让处理模型的人员很清晰地看到CORE DOMAIN了。

所以:把精炼文档做为过程工具

若是精炼文档归纳了CORE DOMAIN的核心元素,那么它就能够做为一个指示器——用以指示模型改变的重要程度。当模型或代码的修改影响到精炼文档时,须要与团队其余成员一块儿协商。当对精炼文档作出修改时,须要当即通知全部团队成员,并且要把心版本的文档分发给他们。CORE外部的修改或精炼文档外部的细节修改则无需协商或通知,能够直接把它们集成到系统中,其余成员在后续工做过程当中天然会看到这些修改。这样开发人员就拥有了XP所建议的彻底的自治性。

 

模式:COHESIVE MECHANISM(内聚机制)

计算有时会很是复杂,使设计开始变得膨胀。机械性的“如何作”大量增长,把概念性的“作什么”彻底掩盖了。为了解决问题提供算法的大量方法掩盖了那些用于表达问题的方法。

所以:把概念上的COHESIVE MECHANISM(内聚机制)分离到一个单独的轻量级框架中。要特别注意公式或那些有完备文档的算法。用一个INTENTION-REVEALING INTERFACE来暴露这个框架的功能。如今,领域中的其余元素就能够只专一于如何表达问题(作什么)了,而把解决方案的复杂细节(如何作)转移给了框架。

GENERIC SUBDOMAIN和COHESIVE MECHANISM有什么不一样?

GENERIC SUBDOMIAN与COHESIVE MECHANISM的动机是相同的——都是为CORE DOMAIN减负。区别在于两者所承担的职责的性质不一样。GENERIC SUBDOMAIN是以描述性的模型做为基础的,它用这个模型表示出团队会如何看待领域的某个方面。在这一点上与CORE DOMIAN没什么区别,只是重要性和专门程度较低而已。COHESIVE MECHANISM并不表示领域,它的目的是解决描述性模型所提出来的一些复杂的计算问题。模型提出问题,COHESIVE MECHANISM解决问题。因此GENERIC SUBDOMAIN是模型级别维度,而COHESIVE MECHANISM是CORE DOMAIN的一部分。

 

模式提高:经过精炼获得声明式风格

声明式设计是一种精简的设计风格,在本书中也多处说起。精炼的价值在于使你可以看到本身正在作什么,不让无关细节分散你的注意力,并经过不断削减获得核心。若是领域中那些起到支持做用的部分提供了一种简练的语言,可用于表示CORE的概念和规则,同时又可以把计算或实施这些概念和规则的方式封装起来,那么CORE DOMAIN的重要部分就能够采用声明式设计。

COHESIVE MACHANISM用途最大的地方是它经过设计一个INTENTION-REVEALING INTERFACE来提供访问,而且具备概念上一致的ASSERTION和SIDE-EFFECT-FREE FUNCTION。利用这些MECHANISM和柔性设计,CORE DOMAIN能够使用有意义的声明,而没必要调用难懂的函数。但最不一样寻常的回报来自于使CORE DOMAIN的一部分产生突破,获得一个深层模型。

把GENERIC SUBDOMAIN提取出来能够减小混乱,而COHESIVE MECHANISM能够把复杂操做封装起来。这样能够获得一个更专一的模型,从而减小了那些对用户活动没什么价值、分散注意力的方面。但咱们不太可能为领域模型中全部非CORE元素安排一个适当的去处。SEGREGATED CORE(分离的核心)采用直接的方法从结构上把CORE DOMAIN划分出来。

 

模式:SEGREGATED CORE(分离的核心)

模型中的元素可能有一部分属于CORE DOMAIN,而另外一部分起支持做用。核心元素可能与通常元素紧密耦合在一块儿。CORE的概念内聚性可能不很强,看上去也不明显。这种混乱性和耦合关系抑制了CORE。设计人员若是没法清晰地看到最重要的关系,就会开发出脆弱的设计。

经过把GENERIC SUBDOMAIN提取出来,能够从领域中清除一些干扰性的细节,使CORE变得更清楚。但识别和澄清全部这些子领域是很困难的工做,并且有些工做看起来并不值得去作。同事,最重要的CORE DOMAIN仍然与剩下的那些元素纠缠在一块儿。

所以:对模型进行重构,把核心概念从支持性元素(包括定义得不清楚的那些元素)中分离出来,并加强CORE的内聚性,同时减小它与其余代码的耦合。把全部通用元素或支持性元素提取出来到其余对象中,并把这些对象放到其余的包中——即便这会把一些紧密耦合的元素分开。

这里基本上采用了与GENERIC SUBDOMAIN同样的原则,只是从另外一个方向考虑而已。就目前来看,使用哪一种简单解决方案均可以,只需把注意力集中在SEGREGATED CORE(分离的核心)上便可。

 

模式:ABSTRACT CORE

一般,即使是CORE DOMAIN模型也会包含太多的细节,以致于它很难表达出总体视图。当不一样MODULE的子领域之间有大量交互时,要么须要在MODULE之间建立不少引用,这在很大程度上抵消了划分模块的价值;要么就必须间接地实现这些交互,然后者会使模型变得晦涩难度。

所以:把模型中最基本的概念识别出来,并分离到不一样的类、抽象类或接口中。设计这个抽象模型,使之可以表达重要组件之间的大部分交互。把这个完整的抽象模型放到它本身的MODULE中,而专用的、详细的实现类则留在由子领域定义的MODULE中。

 

【学习心得】:我很幸运,能遇到一个近作了近十年的大型应用项目,我记得从刚开始只有两台刀片机服务发展至目前百来台高配置级别的PC规模。虽然我如今才看到这本书,但这十年的摸索过程其实就是这章节的实现。实在是很是宝贵的经验。

 

第16章:大型结构

在一个大的系统中,若是由于缺乏一种全局性的原则而令人们没法根据元素在模式(这些模式被应用于整个设计)中的角色来解释这些元素,那么开发人员就会陷入“只见树木,不见森林”的境地。

“大型结构”是一种语言,人们能够用它来从大局上讨论和理解系统。

设计一种应用于整个系统的规则(或角色和关系)模式,令人们能够经过它在必定程度上了解各个部分在总体中所处的位置(即便是在不知道各个部分的详细职责的状况下)。本章将探讨一些能成功构建这种设计结构的模式。


一些大型结构模式
 

模式:EVOLVING ORDER(演化有序)

一个没有任何规则的随意设计会产生一些没法理解总体含义且很难维护的系统。但架构中早期的设计假设又会使项目变得一筹莫展,并且会极大地限制应用程序中某些部分的开发人员/设计人员的能力。很快,开发人员就会为适应结构而不得不在应用程序的开发上委曲求全,要么就是彻底推翻架构而又回到没有协调的开发老路上来。

所以:让这种概念上的大型结构随着应用程序一块儿演变,甚至能够变成一种彻底不一样结构的风格。不要依次过度限制详细的设计和模型决策,这些决策和模型决策必须在掌握了详细以后才能肯定。

于CONTEXT MAP不一样的是,大型结构是可选的。当发现一种大型结构能够明显使系统变得更加清晰,而又没有对模型开发施加一些不天然的约束时,就应该采用这种结构。使用不合适的结构还不如不使用它,所以最好不要为了追求设计的完整性而勉强去使用一种结构,而应该找到尽量精简的方式解决所出现问题。要记住宁缺毋滥的原则。

 

模式:SYSTEM METAPHOR(系统隐喻)

软件设计每每很是抽象且难于掌握。开发人员和用户都须要一些切实可行的方式来理解系统,并共享系统的一个总体视图。

所以:当系统的一个具体类比正好符合团队成员对系统的想象,而且可以引导他们向着一个有用的方向进行思考时,就应该把这个类比用做一种大型结构。围绕这个隐喻来组织设计,并把它吸取到UBIQUITOUS LANGUAGE中。SYSTEM METAPHOR应该既能促进系统的交流,又能指导系统的开发。它能够增长系统不一样部分之间的一致性,甚至能够跨越不一样的BOUNDED CONTEXT。但全部隐喻都不是彻底精确的,所以应不断检查隐喻是否过分或不恰当,当发现它起到妨碍做用时,要随时准备放弃它。

SYSTEM METAPHOR并不适用于全部项目。从整体上讲,大型结构不是必需要用的。在极限编程的12个实践中,SYSTEM METAPHOR的角色能够由UBIQUITOUS LANGUAGE来承担。当项目中发现一种很是适合的SYSTEM METAPHOR或其余大型结构时,应该用它来补充UBIQUITOUS LANGUAGE。

 

模式:RESPONSIBILITY LAYER(职责分层)


自发分层,这些包描述了什么事情

若是每一个对象的职责都是人为分配的,将没有统一的指导原则和一致性,也没法把领域做为一个总体来处理。为了保持大型模型的一致,有必要在职责分配上实施必定的结构化控制。

所以:注意观察模型中的概念依赖性,以及领域中不一样部分的变化频率和变化的缘由。若是在领域中发现了天然的层次结构,就把它们转换为宽泛的抽象职责。这些职责应该描述系统的高层目的和设计。对模型进行重构,使得每一个领域对象,AGGREGATE和MODULE的职责都清晰地位于一个职责层当中。

想要找到一种适当的RESPONSIBILITY LAYER或大比例结构,须要理解问题领域并反复进行实验。若是遵循EVOLVING ORDER,那么最初的起点并非十分重要,尽管差劲的选择确实会加大工做量。结构可能最后演变得面目全非。所以,下面将给出一些指导方针,不管是刚开始选择一种结构,仍是对已有结构进行转换,这些指导方针都适用。

当对层进行删除、合并、拆分和从新定义等操做时,应寻找并保留一下一些有用的特征:

□ 场景描述。层应该可以表达出领域的基本实现或优先级选择一种大比例结构与其说是一种技术决策,不如说是一种业务建模决策。

□ 概念依赖性。“较高”层概念的意义应该依赖“较低”层,而低层概念的意义应该独立于较高层。

□ CONCEPTUAL CONTOUR。若是不一样层的对象必须具备不一样的变化频率或缘由,那么层应该可以允许它们之间的变化。

□ 潜能层。咱们可以作什么?潜能层不关心咱们打算作什么,而关心可以作什么。如企业的资源(包括人力资源)以及这些资源的组织方式是潜能层的核心。

□ 做业层。咱们正在作什么?咱们利用这些潜能作了什么事情?像潜能层同样,这个层也应该反映出现实状况,而不是咱们设想的情况。如咱们但愿在这个层中看到本身的工做和活动:咱们正在销售什么,而不是可以销售什么。一般来讲,做业层对象能够引用潜能层对象,它甚至能够由潜能层对象组成,但潜能层对象不该该引用做业层对象。

□ 决策支持层。应该采起什么行动或制定什么策略?这个层是用来做出分析和制定决策的。它根据来自较低层(如潜能层或做业层)的信息进行分析。决策支持软件能够利用历史信息来主动寻找适用于当前和将来做业的机会

□ 策略层。规则和目标是什么?规则和目标主要是被动的,但它们约束着其余层的行为。这些交互的设计是一个微妙的问题。有时策略会做为一个参数传递给较低层的方法。有时会使用STRATEGY模式。策略层与决策支持层可以进行很好的协做,决策支持层提供了用于搜索策略层所设定的目标的方式,这些目标又受到策略层设定的规则约束。

□ 承诺层。咱们承诺了什么?这个层具备策略层的性质,由于他表述了一些指导将来运营的目标;但它也有做业层的性质,由于承诺是做为后续由于活动的一部分而出现和变化的。

虽然这5个层对不少企业系统都适用,但并非全部领域的主要概念都涵盖在这5个层中。有些状况下,在设计中生硬地套用这种形式反而会起副作用,而使用一组更天然的RESPONSIBILITY LAYER会更有效。

咱们须要对分层结构进行调整和实验,但必定要使分层系统保持简单,若是层数超过4或5,就比较难处理了。层数越多将没法有效地描述领域,并且原本要使用大比例结构解决的复杂性问题又会以一种新的方式出现。咱们必须对大比例结构进行严格的精简。

若是一个领域与上述讨论毫无关系,全部的分层可能都必须从头开始。最后,咱们必须根据直觉选择一个起点,而后经过EVOLVING ORDER来改进它。

 

模式:KNOWLEGE LEVEL(知识级别)


员工工资和养老金系统的原来模型,在新的需求下被过多地约束

若是在一个应用程序中,ENTITY的角色和它们之间的关系在不一样的状况下有很大变化,那么复杂性会显著增长。在这种状况下,不管是通常的模型仍是高度定制的模型,都没法知足用户的需求。为了兼顾各类不一样的情形,对象须要引用其余的类型,或者须要具有一些在不一样状况下包括不一样使用方式的属性。具备相同数据和行为的类可能会大量增长,而这些类的惟一做用只是为了知足不一样的组装规则。

所以:建立一组不一样的对象,用它们来描述和约束基本模型的结构和行为。把这些对象分为两个“级别”,一个是很是具体的级别,另外一个级别则提供了一些可供用户或超级用户定制的规则和知识。

若是获得合理的运用,KNOWLEDGE LEVEL可以解决一些其余方式很难解决的问题。若是系统中某些部分的定制很是关键,而要是不提供定制能力就会破坏掉整个设计,这时就能够利用知识级别来解决这一问题。


Payroll如今已经显示出来,它已与Employee Type分离

像其余大比例结构同样,KNOWLEDGE LEVEL也不是必需要使用的。没有它,对象照样能工做,并且团队可能仍然可以认识到他们须要将Employee与Payroll分离。当项目进行到某个时刻,这种结构看起来已经没什么用了,那么就能够放弃它。

咋看上去,KNOWLEDGE LEVEL像是RESPONSIBILITY LAYER(特别是策略层)的一个特例,但它并非。首先,KNOWLEDGE LEVEL两个级别之间的依赖是双向的,而RESPONSIBILITY LAYER在层次结构中,较低的层不依赖于较高的层。实际上,RESPONSIBILITY LAYER能够与其余大部分的大比例结构共存,它提供了另外一种用来组织模型的维度。

 

模式:PLUGGABLE COMPONENT FRAMEWORK( 可插入式组件框架 )

在深刻理解和反复精炼基础上获得的成熟模型中,会出现不少机会。一般只有在同一个领域中实现了多个应用程序以后,才有机会使用PLUGGABLE COMPONENT FRAMEWORK(可插入式组件框架)。

当不少应用程序须要进行相互操做时,若是应用程序都基于相同的一些抽象,但它们是独立设计的,那么在多个BOUNDED CONTEXT之间的转换会限制它们的集成。各个团队之间若是不能紧密地协做,就没法造成一个SHARED KERNEL。重复和分裂将会增长开发和安装的成本,并且互操做会变得很难实现。

所以:从接口和交互中提炼一个ABSTRACT CORE,并建立一个框架,这个框架要容许这些接口各类不一样实现被自由替换。一样,不管是什么应用程序,只要它严格地经过ABSTRACT CORE的接口进行操做,那么就能够容许它使用这些组件。

PLUGGABLE COMPONENT FRAMEWORK也有几个缺点:

□ 一个缺点是它是一种很是难以使用的模式。它须要高度精确的接口设计和一个很是深刻的模型,以便把一些必要的行为捕获到ABSTRACT CORE中。

□ 另外一个很大的缺点是它只为应用程序提供了有限的选择。若是一个应用程序须要对CORE DOMAIN使用一种很是不一样的方法,那么可插入式组件框架将起到妨碍做用。

 

总结:经过重构获得更适当的结构

一、最小化

二、沟通和自律

三、经过重构获得柔性设计

四、经过精炼能够减轻负担

 

【学习心得】:若是是在持续用心作事的话,每一次重构都是为了比原来的更好,以上的几种模式多少都会触碰获得,若是系统足够“大且运行良好的话。固然,这本书会给了我更加宽广的视野。

 

第17章:领域驱动设计的综合运用

一、把大型结构与BOUNDED CONTEXT结合起来使用


大型结构与BOUNDED CONTEXT结合

在一个CONTEXT中和整个CONTEXT MAP(做为一个总体)是使用同一种结构
 

二、将大型结构与精炼结合起来使用

大型结构和精炼的概念也是互为补充的。大型结构能够帮助解释CORE DOMAIN内部的关系以及GENERIC SUBDOMIAN之间的关系。同时大型结构自己可能也是CORE DOMAIN的一个重要部分。

三、首先评估

当对一个项目进行战略设计时,首先须要清洗地评估现状。

(1)画出CONTEXT MAP。你能画出一个一致的图吗?有没有一些模棱两可的状况?

(2)注意项目上的语言使用。有没有UBIQUITOUS LANGUAGE?这种语言是否足够丰富,以便帮助开发?

(3)理解重点所在。CORE DOMAIN被识别出来了吗?有没有DOMAIN VISION STATEMENT?你能写一个吗?

(4)项目所采用的技术是遵循MODEL-DRIVEN DESIGN,仍是与之相悖?

(5)开发团队是否具有必要的技能?

(6)开发人员是否了解领域知识?他们对领域是否感兴趣?

固然,咱们不会发现完美的答案。咱们如今对项目的了解永远不如未来的了解深刻。但这些问题为咱们提供了一个可靠的起点。

 

四、有谁定制策略

传统上,架构是在应用程序开发开始以前创建的,而且在这种组织中,负责创建架构的团队比应用开发团队拥有更大的权利。但咱们并不必定的遵循这种传统的方式,由于它并不老是十分有效。

战略设计必须明确地应用于整个项目。项目有不少组织方式,这一点我并不想作过多的说明。可是,要想使决策制定过程更有效,须要注意一些基本问题。

一、从应用程序开发自动得出的结构

一个很是善于沟通、懂得自律的团队在没有核心领导的状况下照样可以很好地工做,他们可以遵循EVOLVING ORDER来达成一组共同遵照的原则,这样就可以有机地造成一种秩序,而不用靠命令来约束。

二、以客户为中心的架构团队

当几个团队共用同一种策略时,确实须要集中制定一些决策。架构师若是脱离实际开发工做,就可能会设计出失败的模型,但这是彻底能够避免的。架构团队能够把本身放在与应用开发团队平等的位置上,帮助他们协调大型架构、BOUNDED CONTEXT边界和其余一些跨团队的技术问题。为了在这个过程当中发挥做用,架构团队必须把思考重点放在应用程序的开发上。

 

五、制定战略设计决策的6个要点

一、决策必须传达整个团队;

二、决策过程必须收集反馈意见;

三、计划必须容许演变;

四、架构团队没必要把全部最好、最聪明的人员都吸取进来;

五、战略设计须要遵照简约和谦逊的原则;

六、对象的职责要专注,而开发人员应该是多面手。

技术框架一样如此

技术框架提供了基础设施层,从而使应用程序没必要本身去实现基础服务,并且技术框架还能帮助把领域与其余关注点隔离开,所以它可以极大地加速应用程序(包括领域层)的开发。但技术框架也有风险的,那就是它会影响领域模型实现的表达能力,并妨碍领域模型的自由改变。

不要编写“傻瓜式”的框架。

在划分团队时,若是认为一些开发人员不够聪明,没法胜任设计工做,而让他们去作开发工做,那么这种态度可能会致使失败,由于他们低估了应用程序开发的难度。

注意整体规划

由Christopher Alexander领导的一群建筑师(设计大楼的建筑师)在建筑和城市规划领域中提倡“聚少成多地成长”(piecemeal growth)。他们很是好地解释了整体规划失败的缘由。

    若是没有某种规划过程,那么俄勒冈州大学的校园永远不会像剑桥大学校园那样庞大、和谐而层次分明。

    整体规划是解决这种难题的传统方法。它试图创建足够多的指导方针,来保持总体环境的一致性,同时仍然为每幢建筑保留自由度,并为适应局部须要预留下广阔的空间。

    ······未来这所大学的全部部分将构成一致的总体,由于它们只是被“插入”到整体设计的各个位置中。

    ······实际上整体规划会失败,由于它只是创建了一种极权主义的秩序,而不是一种有机的秩序。它们过于生硬,所以不容易根据天然变化和不可预料的社会生活变化来作出调整。当这些变化发生时······整体规划就过期了,并且再也不被人们遵照。即便人们遵照整体规划······它们也没有足够详细地指定建筑物以前的联系,人口规模、功能均衡等这些用来帮助每幢建筑的局部行为和设计很好地符合总体环境的方面。

    ······试图驾驭这种整体规划过程很是相似于在小孩的填色本上填充颜色······这个过程最多也不过是获得一种极为日常的秩序。

    ······所以,经过整体规划是没法获得一种有机的秩序的,由于这个规划既过于精确,又不够细致。它在总体上过于精确了,而细节上又不够细致。

    ······整体规划的存在疏远了用户[由于,从根本上讲]大部分重要决策已经肯定下来,所以社区成员对社区将来的建设几乎没有什么影响了。

           ——摘自Oregon Experiment,PP. 16-28 [Alexander et al. 1975]

Alexander和他的同事倡议由社区成员共同制定一组原则,并在“聚少成多地成长”的每次行动中多应用这些原则,这样就会造成一种“有机秩序”,而且可以根据环境变化作出调整。

 

【学习心得】:因为我阅读的集中力不足,因此没法很好地从一次阅读中获取系统性的认知。所以,我必须用抄写去深刻我心。特别是一些重要且很重要的知识,我必须这么作。虽然费时费力,但用将来的眼光去看,当下是值得的,再用当下的眼光看将来,原来我如今作的都是对的。笨一点不要紧,时间就这么用的。

相关文章
相关标签/搜索