【DDD】学习笔记-事件风暴与领域分析建模

news2025/1/19 3:03:48

在确定了全景事件流之后,可以在战略设计层面继续精进,鉴别出领域与限界上下文的边界,进入战术设计阶段的领域分析建模。

事件风暴的分析模型要素

通过事件风暴进行领域分析建模,其核心的模型要素就是“事件”。除此之外,参与事件风暴的分析模型要素还包括决策命令、读模型、策略和聚合。其中,事件和策略已经在探索业务全景的时候进行了初步识别。

决策命令

通观事件之起因,除了外部系统是直接发布事件之外,无论是用户活动,还是满足某个条件,都需要一个命令(Command)来响应,它才是直接导致事件发生的“因”。在事件风暴中,Alberto Brandolini 将命令称之为“决策命令(Decision Command)”,使用浅蓝色即时贴表示。决策命令往往由动宾短语组成,例如 Place Order、Send Invitation 等。

由于决策命令和事件存在因果关系,因此二者往往是一一对应的。例如,Cancel Order 决策命令会触发 OrderCancelled 事件,Subscribe Course 决策命令会触发 CourseSubscribed 事件。正是这种一一对应关系,使得它们存在语义上的重叠,区别仅在于时态。故而有的事件风暴实践者认为可以在事件风暴中省略决策命令。我并不敢苟同这一观点,相反,我反而极为强调决策命令在事件风暴中的重要性,它是领域分析建模的一个重要驱动力,因为通过它连接了用户、策略、聚合、读模型和事件,如下图所示:

74748976.png

从图中可以看出,由事件可以驱动出决策命令,在它们之间借由聚合对象来发布事件。当事件发生后,如果某个策略满足条件,也会引发决策命令,而用户在引发决策命令时,需要足够的读模型来帮助它做出正确的决策。

那么,该如何正确地理解决策命令?显然,Alberto Brandolini 使用决策来修饰命令并非空穴来风,因为这一名词突出了命令往往需要更多的信息来帮助参与者(Actor)做出决策。参与者是用例图的设计要素,在事件风暴中,可以认为是对所有事件起因的抽象:用户、条件满足(如定时器)与外部系统。其中,外部系统对我们而言是一个黑盒子,不用考虑它是如何触发了事件,因而可以忽略。因此,参与者在基于业务场景做出决策时,需要如下两方面数据的支撑:

  • 信息:必须基于足够充分的信息才能做出正确的决策,提供这些信息的对象被称之为读模型(Read Model),在事件风暴中用浅绿色即时贴表示。
  • 策略:根据业务规则,当某个条件满足时,会触发一个决策命令,这个业务规则被命名为策略(Policy),在事件风暴中用紫色标签表示。
读模型和策略

当决策命令由用户引发时,可以确认该决策命令的发生是否需要提供足够的读模型信息。读模型是用户通过查询(读)操作获得的。若不具备这一信息,可能不足以支持用户执行决策命令。例如买家希望提交订单,就需要先查看购物车获得购物车内容,然后才能执行下订单(Place Order)的决策命令,触发 OrderCreated 事件。这时,查看购物车获得的结果 ShoppingCart 就是读模型:

img

读模型是用户执行决策命令必需的输入信息,在代码层面,这些读模型就是执行决策命令的领域行为所需的输入参数。用户发起决策命令的方式是因为执行了某个活动,例如决策命令“提交订单”实则是因为用户点击了“提交订单”按钮。用户活动的执行与用户体验(User eXerperience,UX)直接有关。现实世界的业务场景通过用户体验将用户与读模型结合起来,把信息传输给事件风暴的决策命令。这一过程牵涉到用户、查询和命令操作,恰好符合组成用例的要素。若建模人员熟悉用例,也可借助用例图来分析。

注意,上图是将读模型 ShoppingCart 提供给 Place Order 决策命令,而非查询操作与命令操作之间的交互。有的事件风暴实践者将查询操作也纳入到事件风暴的模型中,认为是用户执行查询操作获得读模型后,触发了决策命令,如下图所示:

58315254.png

我认为这样的模型设计并不恰当,因为它将活动流程图与事件的因果关系混为一谈了。实际上,活动流程图反应了现实世界的问题域,事件风暴表现的事件因果关系却是解决方案域的内容,这是领域建模活动中两个不同的层次。买家先查询购物车,然后提交订单,这是买家的操作流程。但从事件的因果关系看,并非“查询购物车”触发了“提交订单”这个决策命令,而是用户通过查询获得了购物车读模型之后,由用户发起“提交订单”的决策命令,再通过订单聚合发布了 OrderCreated 事件。“查询购物车”和“提交订单”是两个不同的用户活动,它们并不具有时序上的连续性,可以认为是两个独立的业务场景。由于查询操作并不会触发事件的发生,从模型上看,它也不会导致命令的发生,因而在事件风暴中,并没有查询操作的位置,而是以读模型的形式出现。这也变相地促使建模人员在识别用户活动时,需要分辨该活动究竟是查询还是命令,有利于 CQRS 模式的落地。

当决策命令由策略引发时,就表示事件发生后某些数据满足了某条业务规则。一旦该策略被满足,就会引起目标对象的状态变更,然后根据业务规则的规定触发下一个决策命令。例如,策略“提交订单后,一旦超过规定时间未支付,则取消订单”会触发 Cancel Order 命令,从而引起 OrderCancelled 事件的发生。策略引发的决策可以是自动的,如定时器检测到支付时间超时;也可以是用户手动触发,如用户登录时输入错误密码的次数太多;还可以二者并存,如在取消订单业务场景中,Cancel Order 命令既可以由定时器自动触发,也可以由用户手动触发。

聚合

虽然决策命令和事件之间存在因果关系,但事件并非直接由决策命令发布,而是借助一个“媒介”来发布事件。这个媒介就是“聚合(Aggregate)”。聚合在事件风暴中使用黄色大即时贴来表示。聚合划分了现实世界和模型世界之间的界线。在现实世界,是用户执行了决策命令触发了事件;在模型世界,是聚合履行了发布事件的职责。例如,在电商系统的业务流程中,现实世界的用户活动是用户提交了订单;在模型世界,是 Order 聚合发布了 OrderCreated 事件。

寻找聚合的过程可能是一个艰难的过程。由于聚合是构成领域分析模型的核心要素,识别聚合需要审慎,不要轻易下结论。若未寻找到它,可以先贴上一个空白的黄色大即时贴表示这里存在一个聚合,但目前还不知道它的名字。

在事件风暴中,我们也可以利用事件来反向寻找聚合。分析事件的特征,由于它是由决策命令触发的,意味着事件的产生会带来目标对象状态的变化。状态的变化分为三种形式:

  • 从无到有:意味着创建,例如“订单已创建”事件标志着新订单的产生。
  • 修改属性值:意味着值的更新,例如“订单已取消”事件使得订单从之前的状态变更为“已取消”状态;也可能意味着内容的变化,例如“商品被加入到购物车”事件,说明购物车增加了一个新的条目。
  • 从有到无:意味着删除,不过在多数项目中并不存在这种状态变化;表面是删除,实际是修改属性值。例如“会员已注销”事件和“商品已下架”事件,实则都不是直接删除会员和商品记录,而是将该记录的状态置为“已注销/已下架”状态。

显然,发生状态变更的对象有很大几率就是我们要寻找的聚合对象。毕竟聚合对象承担了发布事件的职责,而事件又是由于状态变更而产生。谁能准确地侦知状态是否变更以及何时发生变更?我想,只有拥有状态的聚合对象自身才具备这一能力。

事件风暴的领域分析建模过程

显然,围绕着“事件”为中心,事件风暴给出了一条有章可循的领域分析建模路径。领域分析建模的基础是探索业务全景的产出物,即业已识别出来的事件流,以及参与事件流的用户、策略与外部系统。整个领域分析建模的过程如下:

  • 第一步:挑选任意一个与用户有关的事件,反向驱动出决策命令,该用户就是发出决策命令的人(角色)。从事件驱动出决策命令非常容易,就是将事件的过去时态转换为动宾形式的决策命令即可。
  • 第二步:根据决策命令与事件之间的因果关系,推导出要发布该事件必须的前置信息,即决策所需的读模型。读模型通常由用户通过查询操作获得,可以理解为是决策命令行为的输入参数。
  • 第三步:根据事件状态变更的目标,决定决策命令与事件之间的聚合对象。若无法确定,则保留一个空的黄色即时贴,待以后确定。
  • 第四步:选择当前事件的后置事件。若后置事件仍然与用户有关,则重复第一步;若后置事件与外部系统有关,可以跳过该事件的建模,继续选择下一个后置事件。若事件与策略有关,在进一步细化策略对象之后,驱动出决策命令,重复第三步。

以前面所示的信用卡开卡事件流为例,我们依次选择以下三个事件:

img

首先是审批人参与的“开卡申请已审批”事件,执行第一步,由该事件可以反向驱动出决策命令“审批开卡申请”。第二步是根据决策命令推导出触发事件需要的读模型。审批开卡申请的前置信息是“申请”和“用户征信”,若缺乏这两个信息,审批人无法做出“审批开卡申请”的决策。第三步是确定决策命令与事件之间的聚合对象。显然,“开卡申请已审批”事件影响到的就是申请的状态,它就是我们要寻找的聚合对象:

img

接着进入第四步,选择下一个后置事件“卡号已生成”。该事件与策略有关,细化策略为“卡号规则”。由事件驱动出决策命令为“生成卡号”,进入第三步,识别两者之间的聚合对象。卡号的生成影响了信用卡的属性,可以认为该事件影响状态的目标对象为“信用卡”:

64620751.png

继续第四步,选择下一个后置事件“信用卡制作完毕”。由于该事件由外部系统发布,可以忽略该建模过程,仅仅标记外部系统即可:

64725757.png

通过这个简单案例,可以清晰地看到我总结的领域分析建模过程具有一定的可操作性。事件风暴工作坊的参与人员可以按照建模步骤一步一步执行。执行每一步都需要团队与领域专家进一步讨论和确认,保证识别出来的模型对象遵循该领域的统一语言。在这个分析建模过程中,每个模型对象都有着建模的参考依据,包括模型对象的身份特征、彼此之间的关系、承担的职责,这就在一定程度上减轻了对建模人员经验的依赖。

事件风暴的两个层次恰好可以对应领域驱动设计的战略阶段与战术阶段。前者主要用于识别限界上下文,后者主要用于建立领域分析模型,这恰恰填补了 Eric Evans《领域驱动设计》书中的关键空白。当然,Alberto Brandolini 提出的事件风暴不仅于此,它还能用于企业的流程改进、业务创新和对新型服务的探索

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1447439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

InstantBox:开箱即用的临时 Linux 环境

在云计算和虚拟化技术日益成熟的今天,我们有时需要一个快速、简单、临时的 Linux 环境来进行各种任务。这就是 InstantBox 的用武之地。 什么是 InstantBox? InstantBox 是一个开源项目,它可以快速启动临时的 Linux 系统,并提供…

LAXCUS分布式操作系统目标:软件算力入口

英伟达现在的市值相当于整个中国股市!说明了什么? AI 大潮下,算力就是生产力,也是未来 20 年一切产业的基础,英伟达已经把住硬件算力入口,LAXCUS 分布式操作系统瞄准软件算力入口,做好了&#…

【Chrono Engine学习总结】4-vehicle-4.2-车辆轨迹跟踪

由于Chrono的官方教程在一些细节方面解释的并不清楚,自己做了一些尝试,做学习总结。 0、Vehicle的driver driver在上一篇总结中有过介绍,【Chrono Engine学习总结】4-vehicle-4.1-vehicle的基本概念,这里进一步介绍。 对于一个…

C++ //练习 6.6 说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。

C Primer(第5版) 练习 6.6 练习 6.6 说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 /********************…

C语言—数组(2)

在不知道数组类型的情况下,如何确定数组元素的个数? 一维数组:sizeof(a) /sizeof(a[0]) 二维数组:sizeof(a[0])/sizeof(a[0][0]) 当然此次我做的题没体现出来我在末尾写一段演示了一下 因为二维数组也可以看成是一个一维数组 25⬆2.131. 一个二维数组赋了初值&…

Github 2024-02-13 开源项目日报 Top9

根据Github Trendings的统计,今日(2024-02-13统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量JavaScript项目2Python项目2C项目2TypeScript项目2Rust项目1Go项目1Dart项目1Java项目1C项目1 系统设计指南 …

彻底改变单词嵌入和文本分类

目录 一、介绍二、起源与发展三、技术基础四、FastText 的优点五、代码六、结论 一、介绍 2016 年由 Facebook 的 AI Research (FAIR) 团队推出的 FastText 已迅速成为自然语言处理 (NLP) 领域的基石。这种创新的词嵌入和文本分类…

【MySQL】:分组查询、排序查询、分页查询、以及执行顺序

🎥 屿小夏 : 个人主页 🔥个人专栏 : MySQL从入门到进阶 🌄 莫道桑榆晚,为霞尚满天! 文章目录 📑前言一. 分组查询1.1 语法1.2 where与having区别1.3 注意事项:1.4 案例: 二. 排序查询…

力扣精选算法100道——【模板】前缀和 (二维)

目录 🎈题目解析 🎈算法原理 🎈实现代码 二维前缀和【模板】 🎈题目解析 上一题我们讲述了一维的前缀和求法。 第一行三个参数,n是行数3,m是列数4,q3代表查询次数 接下来就是n行m列的矩阵…

剪辑视频衔接怎么操作 剪辑视频衔接过渡自然方法 剪辑视频教程新手入门 抖音剪辑短视频 会声会影视频制作教程

视频剪辑在现代社交媒体和数字媒体时代中变得越来越重要。它广泛应用于各种领域,包括电影制作、广告宣传、教育培训、社交媒体内容创作等。 一、剪辑视频衔接怎么操作 会声会影是一款功能强大、易于使用的视频编辑软件。接下来我们拿会声会影为例讲解剪辑视频如何…

探索设计模式的魅力:捕捉变化的风-用观察者模式提升用户体验

设计模式专栏:http://t.csdnimg.cn/U54zu 目录 一、引言 核心概念 应用场景 可以解决的问题 二、场景案例 2.1 不用设计模式实现 2.2 存在问题 2.3 使用设计模式实现 2.4 成功克服 三、工作原理 3.1 结构图和说明 3.2 工作原理详解 3.3 实现步骤 四、 优…

2024-02-13 Unity 编辑器开发之编辑器拓展4 —— EditorGUIUtility

文章目录 1 EditorGUIUtility 介绍2 加载资源2.1 Eidtor Default Resources2.2 不存在返回 null2.3 不存在则报错2.4 代码示例 3 搜索框查询、对象选中提示3.1 ShowObjectPicker3.2 PingObject3.3 代码示例 4 窗口事件传递、坐标转换4.1 CommandEvent4.2 GUIPoint 和 ScreenPoi…

关于在分布式环境中RVN和使用场景的介绍3

简介 在《关于在分布式环境中RVN和使用场景的介绍2》和《关于在分布式环境中RVN和使用场景的介绍1》中我们介绍了RVN的概念和在一些具体用例中的使用。在本文中我们讨论一下在分布式环境中使用RVN需要注意的问题。 问题 我们在收到一条待处理的事件时,需要检查该…

代码随想录算法训练营Day56|583. 两个字符串的删除操作、72. 编辑距离

目录 583. 两个字符串的删除操作 前言 思路 算法实现 法二 72. 编辑距离 前言 思路 算法实现 总结 583. 两个字符串的删除操作 题目链接 文章链接 前言 本题与上一题不同的子序列相比,变化就是两个字符串都可以进行删除操作了。 思路 利用动规五部曲进…

Ocr之TesseractOcr的安装及使用

目录 一、安装环境 二、安装内容 三、安装过程及识别测试 1. 安装过程 2. 程序编写 总结 1. 安装复杂度较低 2. 国外开源Ocr 3. 可设置识别参数 4. 工具类 一、 系统环境windows 10 linux环境也可安装, 可借鉴此篇文章>> | 二、安装内容 Tesseract exe 程序安…

《金融人工智能:用python实现ai量化交易》

融合了数学、python、深度学习以及金融知识,是本推荐的好书。请收藏本文,读后再给大学总结。

React18原理: 核心包结构与两大工作循环

React核心包结构 1 ) react react基础包,只提供定义 react组件(ReactElement)的必要函数一般来说需要和渲染器(react-dom,react-native)一同使用在编写react应用的代码时, 大部分都是调用此包的api比如, 我们定义组件的时候,就是它提供的class Demo ext…

Elasticsearch:适用于 iOS 和 Android 本机应用程序的 Elastic APM

作者:来自 Elastic Akhilesh Pokhariyal, Cesar Munoz, Bryce Buchanan 适用于本机应用程序的 Elastic APM 提供传出 HTTP 请求和视图加载的自动检测,捕获自定义事件、错误和崩溃,并包括用于数据分析和故障排除目的的预构建仪表板。 适用于 …

LeetCode:83和82.删除排序链表中的重复元素I,II

这两题算是链表的基础题,就遍历删除没啥特点, 83甚至不需要考虑第一个结点的特殊情况,属实是名副其实的easy了 LeetCode:21.合并两个有序链表之第一次的特殊情况-CSDN博客 83. 删除排序链表中的重复元素 - 力扣(Lee…

[NSSCTF]-Web:[SWPUCTF 2021 新生赛]easyrce解析

先看网页 代码审计: error_reporting(0); :关闭报错,代码的错误将不会显示 highlight_file(__FILE__); :将当前文件的源代码显示出来 eval($_GET[url]); :将url的值作为php代码执行 解题: 题目既然允许…