专栏地址

01|领域驱动设计到底在讲什么

软件开发的核心难度在于处理隐藏在业务知识中的复杂度,领域模型就是对这种复杂度的简化和精炼。领域驱动设计是一种模型驱动的设计方法:通过领域模型捕捉领域知识,使用领域模型构造更易维护的软件。
模型在领域驱动设计中主要用途如下:

  • 通过模型反映软件实现的结构
  • 以模型为基础形成团队的统一语言
  • 把模型作为精粹的知识,以用于传递

    提炼领域模型的方法:知识消化法

    知识消化具体步骤:

  • 关联模型与软件实现:是知识消化可以顺利进行的前提与基础。它将模型与代码统一在一起,使得对模型的修改就等同于对代码的修改。

  • 基于模型提取统一语言:将业务方变成模型的使用者,通过统一语言进行需求讨论,实际就是通过模型对需求进行讨论。
  • 开发富含知识的模型
  • 精炼模型
  • 头脑风暴与试验

后三步构成了一个提炼知识的循环:通过统一语言讨论需求;发现模型中的缺失或者不恰当的概念,精炼模型以反映业务的实践情况;对模型的的修改引发统一语言的改变,再以试验和头脑风暴的态度使用新的语言以验证模型的准确。如下图所示:
image.png
知识消化是一种迭代改进试错法,它并不追求模型的好坏,而是通过迭代反馈的方式逐渐提高模型的有效性。这个过程的前提是将模型与软件实现关联在一起。

总结

image.png


02|统一语言

统一语言是一种业务方与技术方共同使用的共同语言,业务方与技术方通过共同语言描述业务规则与需求变动。共同语言为双方提供了协作与沟通的基础。
从模型中提取的统一语言覆盖了领域模型中的概念与逻辑,还提供了必要的补充以帮助业务方理解模型。同时,统一语言也扮演了试验田的角色,其中出现未被提取的知识,将作为提炼知识的循环,逐步完善模型。因而,如下图所示,我们会在提炼知识的循环中使用统一语言而不是模型。
image.png

修改代码就是改变统一语言

修改模型就是修改代码,修改代码就是修改模型。而统一语言是从领域模型中提取的,包含了领域中的概念和逻辑。那么改变了模型,实际上就是改变了统一语言。
统一语言可以包含一下内容:

  • 源自领域模型的概念与逻辑
  • 界限上下文
  • 系统隐喻
  • 职责的分层
  • 模式与惯用法

总结

image.png


03|我们要怎么理解领域驱动设计?

权利与义务的对等构成了协同的基础

技术方通过统一语言获得了定义业务的权利,但是同时也必须承担在提炼知识的循环中接收业务影响实现的义务;
业务方通过提炼知识的循环获得了影响软件实现的权利,但是同样,也有接受技术方通过统一语言定义的业务概念的义务;

价值观体系

  • 领域驱动设计是一种模型驱动的设计方法:模型应处在核心;
  • 两关联一循环:业务与技术围绕着模型的协同。

04|跨越现实的障碍(上):要性能还是要模型?

无法封装的数据库开销

在应用领域驱动设计时,聚合和聚合根是构成“富含知识的模型”的关键。通过聚合关系,我们可以将被聚合的对象的集合逻辑放置于聚合/聚合根里,而不是散落在外,或是放在其他无关的服务中。这么做可以使得逻辑富集于模型中,避免“逻辑泄漏”。但是这些被聚合的的集合对象,通常都是被数据库持久化的集合。也就是说,数据库的引入,网络I/O于其它性能开销无法被接口抽象隔离。而被具体技术实现引入领域模型,则有悖领域驱动设计的理念。

关联对象

将对象间的关联关系直接建模出来,然后在通过接口与抽象的隔离,把具体技术实现细节封装到接口的实现中。

总结

使用关联对象建模替代集合逻辑,并通过接口隔离关联对象的实现细节。


05|跨越现实的障碍(中):富含知识还是代码坏味道?

上下文过载

指领域模型中的某个对象会在多个上下文中发挥重要作用,甚至是聚合根。这个对象本身会变得很复杂,造成模型僵化并且可能会带来潜在的性能问题。
上下文过载的根源:实体在不同的上下文中扮演的多个角色,再借由聚合关系,将不同上下文中的逻辑富集与实体之中,造成了上下文过载。

角色对象和上下文对象

通过使用角色对象和上下文对象将上下文从领域对象中剥离。
过载的上下文
image.png
基于角色上下文对象进行剥离
image.png


06|跨越现实的障碍(下):架构分层就对了吗?

领域驱动设计分层

  • 展现层:负责给最终用户展现信息,并接受用户的输入作为功能的触发点
  • 应用层:负责支撑具体的业务或者交互流程,将业务逻辑组织为软件的功能
  • 领域层:核心的领域概念、信息和规则。它不随应用层的流程、展现层的界面以及基础设施层的能力改变而改变
  • 基础设施层:通用的技术能力,比如数据库、消息总线等。

领域驱动设计使用分层架构,主要是因为各层的需求变化速率(Pace of Changing)不同。分层架构对变化传播的控制,是通过层与层之间的依赖关系实现的,因为下层的修改会波及到上层。我们希望通过层来控制变化的传播,只要所有层都单向依赖比自己更稳定的层,更易变依赖不易改变的,那么变化就不会扩散了。
image.png


07|统一语言可以是领域模型本身吗?

催化剂建模法

催化剂建模法是一种尝试将流程视角引入对象建模的方法。

角色-目标-实体法

  • 与业务方沟通,明确参与系统的所有角色
  • 围绕着这些角色,澄清他们希望通过系统达成的目标
  • 将目标中提及的“实体”(名词、概念)提取出来,问清楚含义和内涵。
  • 围绕这些实体建立领域模型,并与业务方阐述这些模型是如何用以实现目标的
  • 当业务方理解模型之后,模型就成为了统一语言

    08|什么办法可以在讨论中自然形成统一语言

    事件建模法

    事件建模法的两个基本原则:

  • 通过事件表示交互

  • 通过时间线划分不同事件

    事件风暴法

    事件风暴是一种互动式建模工作坊,通过将不同背景的项目参与方汇聚一堂,集思广益从而形成有效的模型。
    从建模方法上来看,事件风暴是一种事件建模法。它以响应式编程(Reactive Programming)作为范式,通过事件、命令与策略之间的响应关系,组织逻辑。事件风暴内各主要概念的相互关联如下图所示:
    image.png

  • 行动者(Actors)是系统的使用者。这里使用者是一个相对模糊的概念,可能是现实中的人也可能是别的系统;

  • 命令(Command)是由行动者发起的行为。它代表了某种决定,通常是事件的起因,也称作行动者触发命令(AIC,Actor Initiated Command);
  • 事件(Event);
  • 聚集(Aggregate)就是领域驱动设计中的聚合,可以看作一组领域对象,在头脑风暴阶段可以泛指某些领域概念,不需要细化;
  • 系统(System)指代的是不需要了解细节的三方系统。因为不需要了解细节,所以我们可以将它们看作一个整体;
  • 阅读模型(Read Model)用以支撑决策的信息。通常与界面布局有关;
  • 策略(Policy)是对于事件的响应,通常表示不属于某些聚集的逻辑。通过策略可以触发新的命令,由策略触发的命令,被称作系统触发命令(SIC,System Initiated Command);

    事件风暴建模的整体流程

  • 首先通过头脑风暴寻找领域事件;

  • 根据事件寻找触发它的命令与行动者;
  • 通过事件,寻找策略以及由策略触发的 SIC;
  • 根据命令与事件,寻找产生了变化的聚合,以及新生成的阅读模型;
  • 根据寻找到的聚合、阅读模型、事件,开始完善、细化领域模型。