# 领域驱动设计

本文针对《领域驱动设计》做章节性节选总结,以便回顾

# 运用领域模型

  • 消化知识
    • 有效的建模要素
      • 模型和实现的绑定
      • 建立一种基于模型的语言
      • 开发一个蕴含丰富知识的模型
      • 提炼模型
      • 头脑风暴和实验
    • 知识消化
      • 学习业务、深入理解模型
    • 持续学习
      • 不断深入学习理解业务,对业务领域专家的方案能持有自己的认知
    • 知识丰富设计:
      • 区分重要的业务规则与普通计算操作(即程序员需要理解领域专家的方案设计)
      • 程序员可以给领域专家展示代码(或技术工件)(即领域专家需要理解程序员的具体实现)

      以便形成反馈闭环

    • 深层模型
      • 在模型构建中后期,在总体思路上会有转变,比如:“对航运业务有了更深刻的认识后,我们并没有删除Itinerary(航线)对象,但模型发生了巨大改变。我们对航运业务的认识从“集装箱在各个地点之间的运输”转变为“运货责任在各个实体之间的传递”。
  • 交流与语言的使用
    • 建模人员之间(建模人员:开发人员、领域专家等)需要建立一套统一的专业术语,即需要通过一个术语能让领域专家和开发人员都能明白其含义
  • 绑定模型和实现
    • 迭代开发过程中,开发需要依据模型进行具体开发,具体实现要能体现模型的设计
    • 迭代过程过程中,模型需要随开发一起演进,而不是一成不变,不合理的需要调整,如果发现模型不实用或者模型存在较大问题,需要重新设计
    • 让用户了解模型,可以更多可能的挖掘系统的潜能

    将建模和编程过程完全分离是行不通的,然而大型项目依然需要技术负责人来协调高层次的设计和建模,并帮助做出最困难或最关键的决策

# 模型驱动设计的构造块

  • 分离领域
    • 要想创建出能够处理复杂任务的程序,需要做到关注点分离——使设计中的每个部分都得到单独的关注。在分离的同时,也需要维持系统内部复杂的交互关系
    • 领域层是精髓
    • 分层模式
      • LAYERED ARCHITECTURE模式,该模式在面向对象的程序中被广泛采用
      • SMART UI模式,该模式是另一种设计方法,与领域驱动设计方法迥然不同且互不兼容

      凡事没有绝对,模式的采用只看适不适用

  • 软件中所表示的模型
    • ENTITY (REFERENCE OBJECT)
      • 很多对象不是通过它们的属性定义的,而是通过连续性和标识定义的
      • 更关注于是哪一个实例
      • 设计标识
    • VALUE OBJECT
      • 很多对象没有概念上的标识,它们描述了一个事务的某种特征
      • 并不关心使用的是VALUE OBJECT的哪个实例
      • 在设计VALUE OBJECT时有多种选择,包括复制、共享或保持VALUE OBJECT不变
    • SERVICE
      • 在某些情况下,最清楚、最实用的设计会包含一些特殊的操作,这些操作从概念上讲不属于任何对象。与其把它们强制地归于哪一类,不如顺其自然地在模型中引入一种新的元素,这就是SERVICE(服务)
  • 领域对象的生命周期
    • 主要两类挑战
      • 在整个生命周期中维护完整性
      • 防止模型陷入管理生命周期复杂性造成的困境当中
    • 面对挑战的办法
      • 首先是AGGREGATE(聚合),它通过定义清晰的所属关系和边界,并避免混乱、错综复杂的对象关系网来实现模型的内聚。聚合模式对于维护生命周期各个阶段的完整性具有至关重要的作用。
      • 接下来,我们将注意力转移到生命周期的开始阶段,使用FACTORY(工厂)来创建和重建复杂对象和AGGREGATE(聚合),从而封装它们的内部结构。
      • 最后,在生命周期的中间和末尾使用REPOSITORY(存储库)来提供查找和检索持久化对象并封装庞大基础设施的手段。

# 通过重构来加深理解

  • 突破
    • 重构过程可能需要冷静的决策:要不要进行重构,这需要项目经理或者研发经理等作出决策
    • 重构可以带来突破,该突破可能会让之后的代码维护越来越轻松,也可能让后续需求变更更少,系统模型更趋于稳定
    • 不要试图去制造突破,这样的刻意的动作可能会让项目陷入困境
    • 为突破不断的做准备,消化知识,精华模型使其更具柔性(参见柔性设计),提炼模型。
    • 不要犹豫着不去做小的改进,这些改进即使脱离不开常规的概念框架,也可以逐渐加深我们对模型理解

    柔性设计除了便于修改,还有助于改进模型本身

  • 将隐式概念转变为显示概念
    • SPECIFICATION 模式
      • SPECIFICATION就是一个谓词,可用来确定对象是否满足某些标准
  • 柔性设计

    为了使项目能够随着开发工作的进行加速前进,而不会由于它自己的老化停滞不前,设计必须要让人们乐于使用,而且易于做出修改。这就是柔性设计(supple)

    • INTENTION-REVEALING INTERFACES 模式
      • 最好的封装,即让使用者不需要去看其内部实现逻辑
    • SIDE-EFFECT-FREE FUNCTION 模式
      • 我们可以宽泛地把操作分为两个大的类别:命令和查询
      • 尽可能把程序的逻辑放到函数中,因为函数是只返回结果而不产生明显副作用的操作
      • 严格地把命令(引起明显的状态改变的方法)隔离到不返回领域信息的、非常简单的操作中
      • 当发现了一个非常适合承担复杂逻辑职责的概念时,就可以把这个复杂逻辑移到VALUE OBJECT中,这样可以进一步控制副作用
    • ASSERTION 模式

      把复杂的计算封装到SIDE-EFFECT-FREE FUNCTION中可以简化问题,但实体仍然会留有一些有副作用的命令,使用这些ENTITY的人必须了解使用这些命令的后果。在这种情况下,使用ASSERTION(断言)可以把副作用明确地表示出来,使它们更易于处理。

      • 把操作的后置条件和类及AGGREGATE的固定规则表述清楚
      • 如果在你的编程语言中不能直接编写ASSERTION,那么就把它们编写成自动的单元测试。还可以把它们写到文档或图中(如果符合项目开发风格的话)
    • CONCEPTUAL CONTOUR 模式
      • 在对功能进行分解还是聚合的探讨中逐渐形成了CONCEPTUAL CONTOUR 模式(概念轮廓)
      • 把设计元素(操作、接口、类和AGGREGATE)分解为内聚的单元,在这个过程中,你对领域中一切重要划分的直观认识也要考虑在内。在连续的重构过程中观察发生变化和保证稳定的规律性,并寻找能够解释这些变化模式的底层CONCEPTUAL CONTOUR。使模型与领域中那些一致的方面(正是这些方面使得领域成为一个有用的知识体系)相匹配
    • STANDALONE CLASS 模式
      • 低耦合是减少概念过载的最基本办法。独立的类是低耦合的极致
      • 消除依赖性并不是说要武断地把模型中的一切都简化为基本类型,这样只会削弱模型的表达能力
    • CLOSURE OF OPERATION 模式

      两个实数相乘,结果仍为实数(实数是所有有理数和所有无理数的集合)。由于这一点永远成立,因此我们说实数的“乘法运算是闭合的”:乘法运算的结果永远无法脱离实数这个集合。当我们对集合中的任意两个元素组合时,结果仍在这个集合中,这就叫做闭合操作。——The Math Forum, Drexel University

      • 在适当的情况下,在定义操作时让它的返回类型与其参数的类型相同。如果实现者(implementer)的状态在计算中会被用到,那么实现者实际上就是操作的一个参数,因此参数和返回值应该与实现者有相同的类型。这样的操作就是在该类型的实例集合中的闭合操作。闭合操作提供了一个高层接口,同时又不会引入对其他概念的任何依赖。
  • 声明式设计

    函数式编程是个很好的例子

    • 柔性设计使得客户代码可以使用声明式的设计风格
    • 分割子领域
    • 利用自己已有的形式
    • 柔性设计的战略价值,我们将把它作为一种工具,用来精炼领域模型,以便使大型和复杂的项目更易于掌握。
  • 应用分析模式
    • 分析模式是一种概念集合,用来表示业务建模中的常见结构。它可能只与一个领域有关,也可能跨越多个领域。
  • 将设计模式应用于模型
    • STRATEGY(也称为POLICY)模式
    • COMPOSITE模式
  • 通过重构得到更深层的理解

# 战略设计

  • 保持模型的完整性
    • BOUNDED CONTEXT 模式
      • 有界上下文,团队间有界界定
      • 减少走灰色地带,不同团队可以不共享模型,实际上需要在共享的代价和收益之间作出权衡,并制定流程来确保其发挥作用。
    • CONTINUOUS INTEGRATION 模式
      • 连续集成
      • 建立一个把所有代码和其他实现工件频繁地合并到一起的过程,并通过自动化测试来快速查明模型的分裂问题。严格坚持使用UBIQUITOUS LANGUAGE,以便在不同人的头脑中演变出不同的概念时,使所有人对模型都能达成一个共识。
    • CONTEXT MAP 模式
      • 上下文映射
      • 识别在项目中起作用的每个模型,并定义其BOUNDED CONTEXT。这包括非面向对象子系统的隐含模型。为每个BOUNDED CONTEXT命名,并把名称添加到UBIQUITOUS LANGUAGE中。描述模型之间的联系点,明确所有通信需要的转换,并突出任何共享的内容。
    • SHARED KERNEL 模式
      • 共享内核
      • 从领域模型中选出两个团队都同意共享的一个子集。当然,除了这个模型子集以外,还包括与该模型部分相关的代码子集,或数据库设计的子集。这部分明确共享的内容具有特殊的地位,一个团队在没与另一个团队商量的情况下不应擅自更改它。
    • CUSTOMER/SUPPLIER DEVELOPMENT TEAM 模式
      • 生产者消费者
    • CONFORMIST 模式
      • 通过严格遵从上游团队的模型,可以消除在BOUNDED CONTEXT之间进行转换的复杂性
    • ANTICORRUPTION LAYER 模式
      • 创建一个隔离层,以便根据客户自己的领域模型来为客户提供相关功能。这个层通过另一个系统现有接口与其进行对话,而只需对那个系统作出很少的修改,甚至无需修改。在内部,这个层在两个模型之间进行必要的双向转换。
    • SEPARATE WAY 模式
      • 分离模式
      • 声明一个与其他上下文毫无关联的BOUNDED CONTEXT,使开发人员能够在这个小范围内找到简单、专用的解决方案。
    • OPEN HOST SERVICE 模式
      • 开放主机模式
      • 定义一个协议,把你的子系统作为一组SERVICE供其他系统访问。开放这个协议,以便所有需要与你的子系统集成的人都可以使用它。当有新的集成需求时,就增强并扩展这个协议,但个别团队的特殊需求除外。满足这种特殊需求的方法是使用一次性的转换器来扩充协议,以便使共享协议简单且内聚。
    • PUBLISHED LANGUAGE 模式
      • 公共语言
      • 把一个良好文档化的、能够表达出所需领域信息的共享语言作为公共的通信媒介,必要时在其他信息与该语言之间进行转换。
  • 精炼
    • 精炼范围
      • 帮助所有团队成员掌握系统的总体设计以及各部分如何协调工作
      • 找到一个具有适度规模的核心模型并把它添加到通用语言中,从而促进沟通
      • 指导重构
      • 专注于模型中最有价值的那部分
      • 指导外包、现成组件的使用以及任务委派。
    • CORE DOMAIN 模式
      • 核心域
      • 对模型进行提炼。找到CORE DOMAIN并提供一种易于区分的方法把它与那些起辅助作用的模型和代码分开。最有价值和最专业的概念要轮廓分明。尽量压缩CORE DOMAIN。
    • GENERIC SUBDOMAIN 模式
      • 一般子域
      • 识别出那些与项目意图无关的内聚子领域。把这些子领域的通用模型提取出来,并放到单独的MODULE中。任何专有的东西都不应放在这些模块中。

      通用不等于可重用

    • DOMAIN VISION STATEMENT 模式
      • 领域愿景声明
      • 写一份CORE DOMAIN的简短描述(大约一页纸)以及它将会创造的价值,也就是“价值主张”。那些不能将你的领域模型与其他领域模型区分开的方面就不要写了。展示出领域模型是如何实现和均衡各方利益的。这份描述要尽量精简。尽早把它写出来,随着新的理解随时修改它。
    • HIGHLIGHTED CORE 模式
      • 高亮核心
    • COHESIVE MECHANISM 模式
      • 内聚机制
      • 把概念上的COHESIVE MECHANISM(内聚机制)分离到一个单独的轻量级框架中。要特别注意公式或那些有完备文档的算法。用一个INTENTION-REVEALING INTERFACE来暴露这个框架的功能。现在,领域中的其他元素就可以只专注于如何表达问题(做什么)了,而把解决方案的复杂细节(如何做)转移给了框架。
    • SEGREGATED CORE 模式
      • 隔离核心
      • 对模型进行重构,把核心概念从支持性元素(包括定义得不清楚的那些元素)中分离出来,并增强CORE的内聚性,同时减少它与其他代码的耦合。把所有通用元素或支持性元素提取到其他对象中,并把这些对象放到其他的包中——即使这会把一些紧密耦合的元素分开。
    • ABSTRACT CORE 模式
      • 抽象核心
      • 通常,即便是CORE DOMAIN模型也会包含太多的细节,以至于它很难表达出整体视图。把模型中最基本的概念识别出来,并分离到不同的类、抽象类或接口中。设计这个抽象模型,使之能够表达出重要组件之间的大部分交互。把这个完整的抽象模型放到它自己的MODULE中,而专用的、详细的实现类则留在由子领域定义的MODULE中。
    • 深层模型精炼
      • 精炼并不仅限于从整体上把领域中的一些部分从CORE中分离出来。它也意味着对子领域(特别是CORE DOMAIN)进行精炼,通过持续重构得到更深层的理解,从而向深层模型和柔性设计推进。精炼的目标是把模型设计得更明显,使我们可以用模型简单地把领域表示出来。深层模型把领域中最本质的方面精炼成一些简单的元素,使我们可以把这些元素组合起来解决应用程序中的重要问题。尽管任何带来深层模型的突破都有价值,但只有CORE DOMAIN中的突破才能改变整个项目的轨道。
    • 选择重构目标
      • 如果采用“哪儿痛治哪儿”这种重构策略,要观察一下根源问题是否涉及CORE DOMAIN或CORE与支持元素的关系。如果确实涉及,那么就要接受挑战,首先修复核心。
      • 当可以自由选择重构的部分时,应首先集中精力把CORE DOMAIN更好地提取出来,完善对CORE的分离,并且把支持性的子领域提炼成通用子领域。
  • 大型结构
    • EVOLVING ORDER 模式
      • 演化顺序
      • 让这种概念上的大型结构随着应用程序一起演变,甚至可以变成一种完全不同的结构风格。不要依此过分限制详细的设计和模型决策,这些决策和模型决策必须在掌握了详细知识之后才能确定。
    • SYSTEM METAPHOR 模式
      • 系统隐喻
      • 隐喻思维在软件开发(特别是模型)中是很普遍的。但极限编程中的“隐喻”却具有另外一种含义,它用一种特殊的隐喻方式来使整个系统的开发井然有序。
      • SYSTEM METAPHOR(系统隐喻)是一种松散的、易于理解的大型结构,它与对象范式是协调的。由于系统隐喻只是对领域的一种类比,因此不同模型可以用近似的方式来与它关联,这使得人们能够在多个BOUNDED CONTEXT中使用系统隐喻,从而有助于协调各个BOUNDED CONTEXT之间的工作。
      • 当系统的一个具体类比正好符合团队成员对系统的想象,并且能够引导他们向着一个有用的方向进行思考时,就应该把这个类比用作一种大型结构。围绕这个隐喻来组织设计,并把它吸收到UBIQUITOUS LANGUAGE中。SYSTEM METAPHOR应该既能促进系统的交流,又能指导系统的开发。它可以增加系统不同部分之间的一致性,甚至可以跨越不同的BOUNDED CONTEXT。但所有隐喻都不是完全精确的,因此应不断检查隐喻是否过度或不恰当,当发现它起到妨碍作用时,要随时准备放弃它。
    • RESPONSIBILITY LAYER 模式
      • 责任层
      • 分层模式有一种变体最适合按职责来分层,我们把这种变体称为RELAXED LAYERED SYSTEM(松散分层系统),如果采用这种分层模式,某一层中的组件可以访问任何比它低的层,而不限于只能访问直接与它相邻的下一层。
      • 注意观察模型中的概念依赖性,以及领域中不同部分的变化频率和变化的原因。如果在领域中发现了自然的层次结构,就把它们转换为宽泛的抽象职责。这些职责应该描述系统的高层目的和设计。对模型进行重构,使得每个领域对象、AGGREGATE和MODULE的职责都清晰地位于一个职责层当中。
    • KNOWLEDGE LEVEL 模式
      • 知识级别(元级别)
      • 创建一组不同的对象,用它们来描述和约束基本模型的结构和行为。把这些对象分为两个“级别”,一个是非常具体的级别,另一个级别则提供了一些可供用户或超级用户定制的规则和知识。
    • PLUGGABLE COMPONENT FRAMEWORK 模式
      • 可插拔模块框架
      • 从接口和交互中提炼出一个ABSTRACT CORE,并创建一个框架,这个框架要允许这些接口的各种不同实现被自由替换。同样,无论是什么应用程序,只要它严格地通过ABSTRACT CORE的接口进行操作,那么就可以允许它使用这些组件。
最近更新时间: 2021/10/11 18:28:22