[水] 《领域驱动设计》关键段落词句(2)

2020.08.14

本文摘取《领域驱动设计》8-17章我认为有意义/重要的段落语句,并列出各节标题
大体来说,《领域驱动设计》8-17章从更高层面上阐述了基于领域知识,进一步组织和精炼1-7章所述Entity、Value Object等模型元素的原则和模式。尤其是14-16章给出了应对较大规模复杂模型的思维和解决方式。此外,柔性设计一章也从“为开发人员服务”的角度强调了代码和模型的可读性和可维护性。

八、突破

8.1 一个关于突破的故事

8.1.1 华而不实的模型

8.1.2 突破

8.1.4 冷静决策

8.1.5 成果

8.2 机遇

8.3 关注根本

8.4 后记:越来越多的新理解

九、将隐式概念转变为显式概念

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

9.1 概念挖掘

9.1.1 倾听语言

9.1.2 检查不足之处

9.1.3 思考矛盾之处

9.1.4 查阅书籍

9.1.5 尝试,再尝试

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

9.2.1 显式的约束

下面是一些警告信号,表明页书的存在正在扰乱其“宿主对象”的设计。

  • 计算约束所需的数据从定义上看并不属于这个对象
  • 相关规则在多个对象中出现,造成了代码重复或导致不属于同一族的对象之间产生了继承关系
  • 很多设计和需求讨论是围绕这些约束进行的,而在代码实现中,它们却隐藏在过程代码中
    如果约束的存在掩盖了对象的基本职责,或者如果约束在领域中非常突出但在模型中却不明显,那么就可以将其提取到一个显式的对象中,甚至可以把它建模为一个对象和关系的集合。

9.2.2 将过程建模为领域对象

9.2.3 模式:Specification

业务规则通常不适合作为Entity或Value Object的职责,而且规则的变化和组合也会掩盖领域对象的基本含义。但是将规则移出领域层的结果会更糟糕,因为这样一来,领域代码就不再表达模型了。

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

为特殊目的创建谓词形式的显式的Value Object。Specification就是一个谓词可用来确定对象是否满足某些标准。

9.2.4 Specification的应用与实现

如果无法把SQL语句在基础设施中创建,还可以重写一个专用的查询方法并把它添加到Invoice Repository中,这样就把SQL语句从领域对象中分离出来了。为了避免在Repository中嵌入规则,必须采用更为通用的方式来表达查询,这种方式不捕捉规则但是可以通过组合或放置在上下文中来表达规则。

十、柔性设计

10.1 模式:Intention-Revealing Interfaces

10.2 模式:Side-Effect-Free Function

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

10.3 模式:Assertion

把操作的后置条件和类及Aggregate的固定规则表述清楚。如果在你的编程语言中不能直接编写Assertion,那么就把它们编写成自动的单元测试。还可以把它们写到文档或图中。

Intention-Revealing Interface清楚地表明了用途,Side-Effect-Free Function和Assertion使我们能够更准确地预测结果,因此封装和抽象更加安全。

10.4 模式:Conceptual Contour

10.5 模式:Standalone Class

10.6 模式:Closure of Operation

10.7 声明式设计

10.8 声明式设计风格

10.9 切入问题的角度

本章展示了一系列技术,它们用于澄清代码意图,使得使用代码的影响变得显而易见,并且解除模型元素的耦合。

10.9.1 分割子领域

10.9.2 尽可能利用已有的形式

十一、应用分析模式

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

十二、将设计模式应用于模型

12.1 模式:Strategy

12.2 模式:Composite

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

把设计模式用作领域模式的唯一要求是这些模式能够描述关于领域概念的一些事情,而不仅仅是作为解决技术问题的技术解决方案。

十三、通过重构得到更深层的理解

13.1 开始重构

13.2 探索团队

13.3 借鉴先前的经验

13.4 针对开发人员的设计

柔性设计能够清楚地表明它的意图。这样的设计使人们很容易看出代码的运行效果,因此很容易预计修改代码的结果。

13.5 重构的时机

13.6 危机就是机遇

十四、保持模型的完整性

14.1 模式:Bounded Context

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

14.2 模式:Continuous Integration

像领域驱动设计中的其他方法一样,Continuous Integration也有两个级别的操作:(1)模型概念的集成;(2)实现的集成。

建立一个把所有代码和其他实现工件频繁地合并到一起的过程,并通过自动化测试来快速查明模型的分裂问题。严格坚持使用Ubiquitous Language,以便在不同人的头脑中演变出不同的概念时,使所有人对模型都能达成一个共识。

14.3 模式:Context Map

Context Map位于项目管理和软件设计的重叠部分。

14.3.1 测试Context的边界

14.3.2 Context Map的组织和文档化

  • Bounded Context应该有名称,以便可以讨论它们。这些名称应该被添加到团队的Ubiquitous Language中。
  • 每个人都应该知道边界在哪里,而且应该能够分辨出任何代码段的Context,或任何情况的Context。

不要说“George团队的呢绒改变了,因此我们也需要改变那些与其进行交互的内容”,而应该说:“Trasport Network模型发生了改变,因此我们也需要修改Booking上下文的转换器。”

14.4 Bounded Context之间的关系

14.5 模式:Shared Kernel

14.6 模式:Customer/Supplier Development Team

14.7 模式:Conformist

14.8 模式:Anticorruption Layer

14.8.1 设计Anticorruption Layer的接口

14.8.2 实现Anticorruption Layer

14.8.3 一个关于防御的故事

14.9 模式:Seperate Way

14.10 模式:Open Host Service

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

14.11 模式:Published Language

14.12 “大象”的统一

14.13 选择你的模型上下文策略

14.14 转换

14.14.1 合并Context:Seperate Way -> Shared Kernel

14.14.2 合并Context:Shared Kernel -> Continuous Integration

14.14.3 逐步淘汰遗留系统

14.14.4 Open Host Service -> Published Language

十五、精炼

15.1 模式:Core Domain

让最有才能的人来开发Core Domain,并据此要求进行相应的招聘。在Core Domain中努力开发能够确保实现系统蓝图的深层模型和柔性设计。仔细判断任何其他部分的投入,看它是否能够支持这个提炼出来的Core.

15.1.1 选择核心

15.1.2 工作的分配

15.2 精炼的逐步提升

15.3 模式:Generic Subdomain

15.3.1 通用不等于可重用

15.3.2 项目风险管理

15.4 模式:Domain Vision Statement

15.5 模式:Highlighted Core

15.5.1 精炼文档

15.5.2 标明Core

15.5.3 把精炼文档作为过程工具

15.6 模式:Cohesive Mechanism

15.6.1 Generic Subdomain与Cohesive Mechanism的比较

Generic Subdomain与Cohesive Mechanism的动机是相同的——都是为Core Domain减负。区别在于二者所承担的职责的性质不同。Generic Subdomain是以描述性的模型作为基础的,它用这个模型表示出团队会如何看待领域的某个方面。在这一点上它与Core Domain没什么区别,只是重要性和专门程度较低而已。Cohesive Mechanism并不表示领域,它的目的是解决描述性模型所提出来的一些复杂的计算问题。

15.6.2 Mechanism是Core Domain的一部分

15.7 通过精炼得到声明式风格

Cohesive Mechanism用途最大的地方是它通过一个Intention-Revealing Interface来提供访问,并且具有概念上一致的Assertion和Side-Effect-Free Function.利用这些Mechanism和柔性设计,Core Domain可以使用有意义的声明,而不必调用难懂的函数。但最不同寻常的回报来自于使Core Domain的一部分产生突破,得到一个深层模型,而且这部分核心领域本身成了一种语言,可以灵活且精确地表达出最重要的应用场景。

15.8 模式:Segregated Core

15.8.1 创建Segregated Core的代价

15.8.2 不断发展演变的团队决策

15.9 模式:Abstract Core

15.10 深层模型精炼

尽管任何带来深层模型的突破都有价值,但只有Core Domain中的突破才能改变整个项目的轨道。

15.11 选择重构目标

十六、大型结构

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

16.1 模式:Evolving Order

让这种概念上的大型结构随着应用程序一同演变,甚至可以变成一种完全不同的结构风格。不要以此过分限制详细的设计和模型决策,这些决策和模型决策必须在掌握了详细知识之后才能确定。

16.2 模式:System Metaphor

当系统的一个具体类比正好符合团队成员对系统的想象,并且能够引导他们向着一个有用的方向进行思考时,就应该把这个类比用作一种大型结构。围绕这个隐喻来组织设计,并把它吸收到Ubiquious Language中。

16.3 模式:Responsibility Layer

16.4 模式:Knowledge Level

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

16.5 模式:Pluggable Componet Framework

从接口和交互中提炼出一个Abstract Core,并创建出一个框架,这个框架允许这些接口的各种不同实现被自由替换。同样,无论是什么应用程序,只要它严格地通过Abstract Core的接口进行操作,那么就可以允许它使用这些组件。

16.6 结构应该有一种什么样的约束

16.7 通过重构得到更适当的结构

16.7.1 最小化

16.7.2 沟通和自律

16.7.3 通过重构得到柔性设计

16.7.4 通过精炼可以减轻负担

十七、领域驱动设计的综合运用

17.1 把大型结构与Bounded Context结合起来使用

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

17.3 首先评估

17.4 由谁制定策略

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

17.4.2 以客户为中心的架构团队

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

  • 决策必须传达到整个团队
  • 决策过程必须收集反馈意见
  • 计划必须允许演变
  • 架构团队不必把所有最好、最聪明的人员都吸收进来
  • 战略设计需要遵守简约和谦逊的原则
  • 对象的职责要专一,而开发人员应该是多面手

17.5.1 技术框架同样如此

17.5.2 注意总体规划