《领域驱动设计》笔记

# 领域驱动设计
## 第一部分:运用领域模型
### 第一章 消化知识
* 有效建模的要素
    * 模型和实现的绑定
    * 建立了一种基于模型的预言
    * 开发一个蕴含丰富知识的模型
    * 提炼模型
    * 头脑风暴和实验
* 知识消化
    * 研发人员和领域专家一起参与领域知识的消化,不断重塑模型,最终获得一组适用于所有细节的抽象概念
* 持续学习
    * 高效的团队需要有意识地积累知识,并持续学习。对于开发人员来说,这意味着既要完善技术知识,也要培养一般的领域建模技巧,还包括正在从事的特定领域的知识
* 知识丰富的设计
* 深层模型
    * 有用的模型很少停留在表面。随着对领域和应用程序需求的理解逐步加深,我们往往会丢弃那些最初看起来很重要的表面元素,或者切换它们的角度。这时,一些开始不可能发现的巧妙抽象就会渐渐浮出水面,而它们恰恰切中问题的要害
### 第二章 交流与语言的使用
* 模型:Ubiquitous Language
    * 领域模型可成为软件项目通用语言的核心
    * 开发人员应该使用基于模型的预言来描述系统中的工作、任务和功能。这个模型应该为开发人员和领域专家提供一种用于相互交流的语言,而且领域专家还应该使用这种语言来讨论需求、开发计划和特性。语言使用得越普遍,理解进行得就越顺畅。
    * 将模型作为语言的支柱。确保团队在内部的所有交流中以及代码中坚持使用这种语言。在画图、写东西,特别是讲话时也要使用这种语言。
    * 概要: 在英语作为母语的国家,领域语言可直接用到开发中;而在中文里,领域语言需要转换为英文用到开发中。因此领域的中英文对照非常重要,英文可以词不达意,但是必须要能够在团队内部做到见英文知模型
* “大声地”建模
    * 讨论系统时要结合模型,使用模型元素及其交互大声描述场景,并且按照模型允许的方式将各种概念结合到一起
* 一个团队,一种语言
    * 有了Ubiquitous Language之后,开发人员之间的对话、领域专家之间的讨论以及代码本身所表达的内容都基于同一语言,都来自于一个共享的领域模型
* 文档和图
    * 书面设计文档
        * 文档应作为代码和口头交流的补充
        * 文档应保持鲜活并保持最新
    * 完全依赖可执行代码的情况
        * 完全依赖可执行代码而没有文档的情况下,代码必须基于在编写需求时所使用的同一种语言
* 解释性模型
    * 领域模型是领域问题的一个视图,是领域中最为核心的内容。为了更好地学习领域,可以引入解释性模型。解释性模型作为领域模型的一种补充,阐述领域内范围更广的知识。
### 第三章 绑定模型和实现
* 模式:Model-Driven Design
    * 软件系统各个部分的设计应该忠实地反映领域模型,以便体现出这两者之间的明确对应关系。我们应该反复检查并修改模型,以便软件可以更加自然地实现模型,即使想让模型反映出更深层次的领域概念时也应如此。我们需要的模型不但应该满足这两种需求,还应该能够支持健壮的Ubiquitous Language。
* 模式:Hands-On Modeler
    * 任何参与建模的技术人员,不管在项目中的主要职责是什么,都必须花时间了解代码。任何负责修改代码的人员则必须学会用代码来表达模型。每一个开发人员都必须不同程度地参与模型讨论并与领域专家保持联系。参与不同工作的人都必须有意识地通过Ubiquitous Language与接触代码的人及时交换关于模型的想法。
### 概要: Model-Driven Design利用模型来为应用程序解决问题。项目组通过知识消化将大量杂乱无章的信息提炼成实用的模型。而Model-Driven Design将模型和程序实现过程紧密结合。Ubiquitous Language则成为开发人员、领域专家和软件产品之间传递信息的渠道。
最终的软件产品能够在完全理解核心领域的基础上提供丰富的功能。
## 第二部分:模型驱动设计的构造块
### 第四章 分离领域
* 模式:Layered Architecture
    * 分层和领域模型
        * 给复杂的应用程序划分层次。在每一层内分别进行设计,使其具有内聚性并且只依赖于它的下层。采用标准的架构模式,只与上层进行松散的耦合。将所有与领域模型相关的代码放在一个层中,并把它与用户界面层、应用层以及基础设施层的代码分开。领域对象应该将重点放在如何表达领域模型上,而不需要考虑自己的显示和存储问题,也无需管理应用任务等内容。这使得模型的含义足够丰富,结构足够清晰,可以捕捉到基本的业务知识,并有效地使用这些知识。
    * 将各层关联起来
        * 各层之间的松散连接的,层与层的依赖关系只能是单向的。上层可以直接使用或操作下层元素,方法是通过调用下层元素的公共接口,保持对下层元素的引用,以及采取常规的交互手段。而如果下层元素需要对上层元素进行通信,则需要另一种通信机制,使用架构模式来连接上下层,如回调或订阅者模式。
    * 架构框架
        * 好的框架既能解决复杂技术问题,也能让开发人员集中精力去表达模型,而不考虑其他问题
* 领域层是模型的精髓
### 第五章 软件中所表示的模型
* 关联
    * 对象之间的关联使得建模与实现之间的交互更为复杂
    * 使关联更易控制的方法
        * 1)规定一个遍历方向
        * 2)添加一个限定符,以便有效地减少多重关联
        * 3)消除不必要的关联
* 模式:Entity
    * 主要由标识定义的对象被称作Entity。
    * 当一个对象由其标识(而不是属性)区分时,那么在模型中应该主要通过标识来确定该对象的定义。使类定义变得简单,并集中关于生命周期的连续性和标识。
    * 在定义标识操作时,要确保这种操作为每个对象生成唯一的结果,这可以通过附加一个保证唯一性的符号来实现。这种定义标识的方法可以来自外部,也可能是由系统生成的。
    * 要格外注意那些通过通过属性来匹配对象的需求,模型必须定义出“符合什么条件才算是相同的事物”。
* 模式:Value Object
    * 用于描述领域的某个方面而本身没有概念标识的对象成为Value Object。
    * 当我们只关心一个模型元素的属性时,应该把它归类为Value Object。我们应该使这个模型元素能够表示出其属性意义,并为它提供相关功能。Value Object应该是不可变的。不要为它分配任何标识,而且不要把它设计成像Entity那么复杂。
* 模式:Service
    * Service为过程建模
    * 当领域中的某个重要的过程或转换操作不是Entity或Value Object的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为Service。定义接口时要使用模型语言,并确保操作名称是Ubiquitous Language中的术语。此外,应该使Service成为无状态的。
* 模式:Module
    * 选择能够描述系统的MODULE,并使之包含一个内聚的概念集合。这通常会实现MODULE之间的低耦合,但如果效果不理想,则应寻找一种更改模型的方式来消除概念之间的耦合,或者找到一个可作为MODULE基础的概念(这个概念先前可能被忽视了),基于这个概念组织的MODULE可以以一种有意义的方式将元素集中到一起。找到一种低耦合的概念组织方式,从而可以相互独立地理解和分析这些概念。对模型进行精化,直到可以根据高层领域概念对模型进行划分,同时相应的代码也不会产生耦合。
### 第六章 领域对象的生命周期
* 在DDD中管理对象的生命周期有两个挑战
    * (1) 在整个生命周期中维护完整性。
    * (2) 防止模型陷入管理生命周期复杂性造成的困境当中。
* 模式:Aggregate
    * 我们应该将ENTITY 和VALUE OBJECT 分门别类地聚集到AGGREGATE 中, 并定义每个AGGREGATE的边界。在每个AGGREGATE中,选择一个ENTITY作为根,并通过根来控制对边界内其他对象的所有访问。只允许外部对象保持对根的引用。对内部成员的临时引用可以被传递出去,但仅在一次操作中有效。由于根控制访问,因此不能绕过它来修改内部对象。这种设计有利于确保AGGREGATE中的对象满足所有固定规则,也可以确保在任何状态变化时AGGREGATE作为一个整体满足固定规则。
* 模式:Factory
    * Factory的职责
        * 应该将创建复杂对象的实例和AGGREGATE的职责转移给单独的对象,这个对象本身可能没有承担领域模型中的职责,但它仍是领域设计的一部分。提供一个封装所有复杂装配操作的接口,而且这个接口不需要客户引用要被实例化的对象的具体类。在创建AGGREGATE时要把它作为一个整体,并确保它满足固定规则。
    * 工厂需满足两个基本需求
        * (1) 每个创建方法都是原子的,而且要保证被创建对象或AGGREGATE的所有固定规则。
        * (2) FACTORY应该被抽象为所需的类型,而不是所要创建的具体类。
* 模式:Repository
    * 为每种需要全局访问的对象类型创建一个对象,这个对象相当于该类型的所有对象在内存中的一个集合的“替身”。通过一个众所周知的全局接口来提供访问。提供添加和删除对象的方法,用这些方法来封装在数据存储中实际插入或删除数据的操作。提供根据具体条件来挑选对象的方法,并返回属性值满足查询条件的对象或对象集合(所返回的对象是完全实例化的),从而将实际的存储和查询技术封装起来。只为那些确实需要直接访问的AGGREGATE根提供REPOSITORY。让客户始终聚焦于模型,而将所有对象的存储和访问操作交给REPOSITORY来完成。
## 第三部分:通过重构来加深理解
### 重构的层次
* (1)偏技术的修改代码,以使其更具可读性或在非常细节的层次上有所改进
* (2)利用成熟的设计模式进行开发,“通过重构得到模式”
* (3)为实现更深层模型而进行的重构。源于对领域的新认知,通过代码清晰地表达出模型的含义。
### 第九章 将隐式概念转变为显式概念
* 概念挖掘
    * (1)倾听领域专家使用的语言。
    * (2)场景走查,找出不足之处。
    * (3)思考矛盾之处。
    * (4)查阅书籍。
    * (5)尝试,再尝试。
* 为不太明显的概念建模
    * 为显式的约束建模
        * 模式:Specification
    * 将过程建模为领域对象
### 第十章 柔性设计
* 过度过早的设计达不到很好的扩展性
* 模式:Intention-Revealing Interfaces(释意接口)
    * 当我们把概念显式地建模为类或方法时,为了真正从中获取价值,必须为这些程序元素赋予一个能够反映出其概念的名字。类和方法的名称为开发人员之间的沟通创造了很好的机会,也能够改善系统的抽象。类型名称、方法名称和参数名称组合在一起,共同形成了一个INTENTION-REVEALING INTERFACE(释意接口)。
    * 在命名类和操作时要描述它们的效果和目的,而不要表露它们是通过何种方式达到目的的。这样可以使客户开发人员不必去理解内部细节。这些名称应该与UBIQUITOUS LANGUAGE保持一致,以便团队成员可以迅速推断出它们的意义。在创建一个行为之前先为它编写一个测试,这样可以促使你站在客户开发人员的角度上来思考它。
* 模式:Side-Effect-Free Function
    * 尽可能把程序的逻辑放到函数中,因为函数是只返回结果而不产生明显副作用的操作。严格地把命令(引起明显的状态改变的方法)隔离到不返回领域信息的、非常简单的操作中。当发现了一个非常适合承担复杂逻辑职责的概念时,就可以把这个复杂逻辑移到VALUE OBJECT中,这样可以进一步控制副作用。
* 模式:Assertion(断言)
    * 把操作的后置条件和类及AGGREGATE的固定规则表述清楚。如果在你的编程语言中不能直接编写ASSERTION,那么就把它们编写成自动的单元测试。还可以把它们写到文档或图中(如果符合项目开发风格的话)。
寻找在概念上内聚的模型,以便使开发人员更容易推断出预期的ASSERTION,从而加快学习过程并避免代码矛盾。
* 模式:Conceptual Contour(概念轮廓)
    * 把设计元素(操作、接口、类和AGGREGATE)分解为内聚的单元,在这个过程中,你对领域中一切重要划分的直观认识也要考虑在内。在连续的重构过程中观察发生变化和保证稳定的规律性,并寻找能够解释这些变化模式的底层CONCEPTUAL CONTOUR。使模型与领域中那些一致的方面(正是这些方面使得领域成为一个有用的知识体系)相匹配。
* 模式:Standalone Class
    * 低耦合是对象设计的一个基本要素。尽一切可能保持低耦合。把其他所有无关概念提取到对象之外。这样类就变得完全独立了,这就使得我们可以单独地研究和理解它。每个这样的独立类都极大地减轻了因理解MODULE而带来的负担。
    * 低耦合是减少概念过载的最基本办法。独立的类是低耦合的极致。
* 模式:Closure Of Operation
### 第十二章 将设计模式应用于模型
* 为了在领域驱动设计中充分利用部分设计模式,我们必须同时从两个角度看待它们:从代码的角度来看它们是技术设计模式,从模型的角度来看它们就是概念模式。
* 模式:Strategy(也称为Policy)
    * 我们需要把过程中的易变部分提取到模型的一个单独的“策略”对象中。将规则与它所控制的行为区分开。按照STRATEGY设计模式来实现规则或可替换的过程。策略对象的多个版本表示了完成过程的不同方式。
    * 作为设计模式的STRATEGY侧重于替换不同算法的能力,而当其作为领域模式时,其侧重点则是表示概念的能力,这里的概念通常是指过程或策略规则。
* 模式:Composite
    * 定义一个把Composite的所有成员都包含在内的抽象类型。在容器上实现那些查询信息的方法时,这些方法返回由容器内容所汇总的信息。而“叶”节点则基于它们自己的值来实现这些方法。客户只需使用抽象类型,而无需区分“叶”和容器。
### 第十三章 通过重构得到更深层的理解
* 通过重构得到深层次模型,必须要关注的
    * (1) 以领域为本;
    * (2) 用一种不同的方式来看待事物;
    * (3) 始终坚持与领域专家对话。
* 重构的时机
    * (1)设计没有表达出团队对领域的最新理解;
    * (2)重要的概念被隐藏在设计中了(而且你已经发现了把它们呈现出来的方法);
    * (3)发现了一个能令某个重要的设计部分变得更灵活的机会。
## 第四部分:战略设计
### 概要
* 在大规模的系统中采用领域驱动设计方法,也不要脱离实现去开发模型。每个决策都必须对系统开发产生直接的影响,否则它就是无关的决策。战略设计原则必须指导设计决策,以便减少各个部分之间的互相依赖,在使设计意图更为清晰的同时而又不失去关键的互操作性和协同性。战略设计原则必须把模型的重点放在捕获系统的概念核心,也就是系统的“远景”上。而且在完成这些目标的同时又不能为项目带来麻烦。为了帮助实现这些目标,这一部分探索了3个大的主题:上下文、精炼和大型结构。
* 上下文
    * 通过为每个模型显式地定义一个BOUNDED CONTEXT,然后在必要的情况下定义它与其他上下文的关系,建模人员就可以避免模型变得缠杂不清。
* 精炼
    * 通过精炼可以减少混乱,并且把注意力集中到正确的地方。
* 大型结构
    * 大型结构是用来描述整个系统的。
### 第十四章 保持模型的完整性
* 模式:Bounded Context
    * 明确地定义模型所应用的上下文。根据团队的组织、软件系统的各个部分的用法以及物理表现(代码和数据库模式等)来设臵模型的边界。在这些边界中严格保持模型的一致性,而不要受到边界之外问题的干扰和混淆。
* 模式:Continuous Integration
    * 建立一个把所有代码和其他实现工件频繁地合并到一起的过程,并通过自动化测试来快速查明模型的分裂问题。严格坚持使用UBIQUITOUS LANGUAGE,以便在不同人的头脑中演变出不同的概念时,使所有人对模型都能达成一个共识。
* 模式:Context Map
    * 识别在项目中起作用的每个模型,并定义其BOUNDED CONTEXT。这包括非面向对象子系统的隐含模型。为每个BOUNDED CONTEXT命名,并把名称添加到UBIQUITOUS LANGUAGE中。
描述模型之间的联系点,明确所有通信需要的转换,并突出任何共享的内容。
先将当前的情况描绘出来。以后再做改变。
* Bounded Context之间的关系
    * 模式:Shared Kernel
        * 从领域模型中选出两个团队都同意共享的一个子集。当然,除了这个模型子集以外,还包括与该模型部分相关的代码子集,或数据库设计的子集。这部分明确共享的内容具有特殊的地位,一个团队在没与另一个团队商量的情况下不应擅自更改它。
        * 功能系统要经常进行集成,但集成的频率应该比团队中CONTINUOUS INTEGRATION的频率低一些。在进行这些集成的时候,两个团队都要运行测试。
    * 模式:Customer/Supplier Development Team
        * 在两个团队之间建立一种明确的客户/供应商关系。
        * 两个团队共同开发自动化验收测试,用来验证预期的接口。
    * 模式:Conformist(跟随者)
        * 如果上游设计的质量不是很差,而且风格也能兼容的话,那么最好不要再开发一
        * 个独立的模型。这种情况下可以使用Conformist(跟随者)模式。
    * 模式:Anticorruption Layer
        * 当正在构建的新系统与另一个系统的接口很大时,为了克服连接两个模型而带来的困难,可以创建一个隔离层,以便根据客户自己的领域模型来为客户提供相关功能。这个层通过另一个系统现有接口与其进行对话,而只需对那个系统作出很少的修改,甚至无需修改。在内部,这个层在两个模型之间进行必要的双向转换。
        * 使用Facade、Adapter和转换器
### 第十五章 精炼
* 领域模型的战略精炼包括
    * (1) 帮助所有团队成员掌握系统的总体设计以及各部分如何协调工作;
    * (2) 找到一个具有适度规模的核心模型并把它添加到通用语言中,从而促进沟通;
    * (3) 指导重构;
    * (4) 专注于模型中最有价值的那部分;
    * (5) 指导外包、现成组件的使用以及任务委派。
* 模式:Core Domain
    * 对模型进行提炼。找到CORE DOMAIN并提供一种易于区分的方法把它与那些起辅助作用的模型和代码分开。最有价值和最专业的概念要轮廓分明。尽量压缩CORE DOMAIN。
    * 让最有才能的人来开发CORE DOMAIN,并据此要求进行相应的招聘。在CORE DOMAIN中努力开发能够确保实现系统蓝图的深层模型和柔性设计。仔细判断任何其他部分的投入,看它是否能够支持这个提炼出来的CORE。
* 模式:Generic Subdomain
    * 识别出那些与项目意图无关的内聚子领域。把这些子领域的通用模型提取出来,并放到单独的Module中。任何专有的东西都不应放在这些模块中。
    * 把它们分离出来以后,在继续开发的过程中,它们的优先级应低于Core Domain的优先级,并且不要分派核心开发人员来完成这些任务(因为他们很少能够从这些任务中获得领域知识)。此外,还可以考虑为这些Generic Subdomain使用现成的解决方案或“公开发布的模型”(Published Model)。
* 模式:Highlighted Core
    * 精炼文档
    * 标明Core
    * 把精炼文档作为过程工具
* 模式:Cohesive Mechanism
    * 把概念上的Cohesive Mechanism(内聚机制)分离到一个单独的轻量级框架中。要特别注意公式或那些有完备文档的算法。用一个Intention-Revealing Interface来暴露这个框架的功能。现在,领域中的其他元素就可以只专注于如何表达问题(做什么)了,而把解决方案的复杂细节(如何做)转移给了框架。
* 模式:Segregated Core
    * 对模型进行重构,把核心概念从支持性元素(包括定义得不清楚的那些元素)中分离出来,并增强CORE的内聚性,同时减少它与其他代码的耦合。把所有通用元素或支持性元素提取到其他对象中,并把这些对象放到其他的包中——即使这会把一些紧密耦合的元素分开。
### 第十六章 大型结构
* 模式:Evolving Order
    * 大型结构随着应用程序一起演变,甚至可以变成一种完全不同的结构风格。不要依此过分限制详细的设计和模型决策,这些决策和模型决策必须在掌握了详细知识之后才能确定。
* 模式:Responsibility Layer
    * 注意观察模型中的概念依赖性,以及领域中不同部分的变化频率和变化的原因。如果在领域中发现了自然的层次结构,就把它们转换为宽泛的抽象职责。这些职责应该描述系统的高层目的和设计。对模型进行重构,使得每个领域对象、AGGREGATE和MODULE的职责都清晰地位于一个职责层当中。
* 通过重构得到更适当的结构
    * 最小化
    * 沟通和自律
    * 通过重构得到柔性设计
    * 通过精炼可以减轻负担
### 概要: BOUNDED CONTEXT(限界上下文)定义了每个模型的应用范围,而CONTEXT MAP(上下文图)则给出了项目上下文以及它们之间关系的总体视图。这些降低模糊性的技术能够使项目更好地进行,但仅仅有它们还是不够的。一旦确立了CONTEXT的边界之后,仍需要持续集成这种过程,它能够使模型保持统一。
其后,在这个稳定的基础之上,我们就可以开始实施那些在界定和关联CONTEXT方面更有效的策略了—从通过共享内核(SHARED KERNEL)来紧密关联上下文,到那些各行其道(SEPARATE WAYS)地进行松散耦合的模型。
## 概要: 上下文、精炼和大型结构这3种原则各有各的用处,但结合起来使用将发挥更大的力量,遵守这些原则就可以创建出好的设计,即使是对一个非常庞大的没有人能够完全理解的系统也是如此。
大型结构能够保持各个不同部分之间的一致性,从而有助于这些部分的集成。结构和精炼能够帮助我们理解各个部分之间的复杂关系,同时保持整体视图的清晰。BOUNDED CONTEXT使我们能够在不同的部分中进行工作,而不会破坏模型或是无意间导致模型的分裂。把这些概念加进团队的UBIQUITOUS LANGUAGE中,可以帮助开发人员找出他们自己的解决方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值