DDD - 事件风暴从理论到落地

简介: DDD - 事件风暴从理论到落地


1. 什么是事件风暴

事件风暴是由 Alberto Brandolini 发明的一种轻量级,基于 DDD 概念的系统分析方法,EventStorming官网是这么说的:

事件风暴(EventStorming)是一种以研讨会(workshop,也有翻译为“工作坊”的)的形式开展的,用于协作探索和研究业务领域,特别是具有复杂流程的业务领域的系统分析方法

它有不同的风格,可以在不同的场景中使用:

  1. 评估现有业务线的健康状况并发现最有效的改进领域;
  2. 探索新的业务模式的可行性;
  3. 设想新的服务,最大限度地为所有相关方带来积极成果;
  4. 设计整洁可维护的事件驱动型(Event-Driven)软件,以支持快速发展的业务。

事件风暴的自适应特性,决定了它允许有不同背景的项目干系人进行复杂的,跨学科的沟通交流,提供了一种跨越信息孤岛和专业界限的新型协作方式。

事件风暴正是 DDD 战略设计中经常使用的一种方法,它可以快速分析和分解复杂的业务领域,完成领域建模

2. 准备工作

在开始事件风暴之前,需要准备好相关物料,邀请相关的参与人员,具体如下:

2.1 邀请参与者

事件风暴采用工作坊的方式,将项目团队领域专家聚集在一起,通过可视化、高互动的方式一步一步将领域模型设计出来。领域专家是事件风暴中必不可少的核心参与者。领域专家可以是:业务人员、需求分析人员、产品经理或者在这个领域有多年经验的开发人员。

除了领域专家,事件风暴的其他参与者可以是 DDD 专家、架构师、产品经理、项目经理、开发人员、测试人员和UI设计师 等项目团队成员。

领域建模是统一团队语言的过程,因此项目团队应尽早地参与到领域建模中,这样才能高效建立起团队的通用语言。到了微服务建设时,领域模型也更容易和系统架构保持一致。

2.2 准备材料

事件风暴参与者会将自己的想法意见写在即时贴上,并将贴纸贴在墙上的合适位置,我们

称这个过程是“刷墙”。

在这个过程中,我们要用不同颜色的贴纸区分领域行为

当然,颜色并不固定,团队内统一才是重点。

2.3 准备场地

一面足够大的墙一块足够大的空间,比如说大会议室。墙是用来贴纸的,大空间可以让人四处走动,方便合作。撤掉会议桌和椅子的事件风暴,你会发现参与者们的效率更高。

事件风暴的发明者曾经建议要准备八米长的墙,这样设计就不会受到空间的限制了。当然,这个不是必要条件,看各自的现实条件吧,不要让思维受限就好。

2.4 分析的关注点

在领域建模的过程中,我们需要重点关注这类业务的语言行为。比如某些业务动作或行为(事件)是否会触发下一个业务动作,这个动作(事件)的输入和输出是什么?是谁(实体)发出的什么动作(命令),触发了这个动作(事件)… 我们可以从这些暗藏的词汇中,分析出领域模型中的事件、命令和实体等领域对象。

3. 事件风暴中的概念

3.1 事件Event

领域事件(Domain Event):是领域专家关心的,在业务上真实发生的事件,这些事件对系统会产生重要的影响,如果没有这些事件的发生,整个业务逻辑和系统实现就不能成立。我们可以通过领域事件对过去发生的事情进行溯源,因为过去所发生的对业务有意义的信息都会通过某种形式保存下来。领域事件在技术实现过程中可以被实现为专门的事件类,便于实现事件驱动设计(Event Driven)。

注意:一般查询操作都不会触发事件的产生,所以查询操作不是事件,如点击了查询按钮显示了数据列表,一般情况下不会有类似于:数据已查询或列表已查询,这样的事件。事件是对系统产生了业务上的影响的动作,相对的,如果仅仅是数据查询操作,则只会对系统产生技术上的影响,如CPU上升或者内存上升等。

常见的影响有:

  • 对内:产生了某种数据、触发了某种流程或事情状态发生了某种变化;
  • 对外:发送了某些消息;
  • 规则(Policy):是对分支条件或复杂业务规则的抽象,目的是通过降低分支复杂度聚焦主要业务流程,未来在技术实现时可能是一些分支条件,也可能应用适合的设计模式。

使用正方形橘黄色的贴纸表示,事件使用正方形橘黄色的贴纸表示,并且是过去式,如:商品已创建(Goods created),库存已增加(Inventory Increase)等。

3.2 热点Hotspot(IDEAS, RISKS)

热点表示不确定的点、有风险的点或者需要特别注意的点,一般贴在事件旁边,代表这件事情值得特别关注。

使用紫色的贴纸表示。

3.3 决策命令Command

决策命令产生了事件,可理解为产生事件的动作,与事件一一对应。如商品已创建(Goods created) 事件对应的决策命令就是添加商品(ADD Goods)

决策命令用正方形蓝色贴纸表示,在实践中只需要将事件“反过来”就行了。

3.4 发起命令的参与者User/Actor

前面的决策命令一定是由某个人或系统来发起的。比如:前面的添加商品这个命令,是由运营人员这个Actor发起的,进而可以联想到可能整个系统中还会有客服、产品经理等。

注意:在Big-Picture Event Storming workshop的实践中,不将决策命令贴出来

使用小长方形亮黄色贴纸,结合Command和Event。

3.5 外部系统External System和规则Policy

Event不一定由前面所说的某个Actor触发Command而产生,也可能是由外部系统或者某种规则自动触发Command而产生。

外部系统使用大长方形的粉红色的贴纸表示,处理规则Policy使用大长方形的紫色的贴纸表示。

3.6 读模型Read Model

某个Actor做出决策Command的前提是需要看到某些信息,或者说,支撑Actor更容易做出决策命令Command的信息。读模型一般是通过Web页面(UI/UX)来展示更多的信息,以让用户更容易做出决策。

读模型用绿色正方形的贴纸来表示。

3.7 聚合Aggregate

某个Actor在某个聚合调用某种Command产生了某个Event。比如前面的商品已创建事件,是由 运营人员商品管理 上调用 添加商品 这个命令而产生。

聚合使用大长方形的黄色的贴纸表示。

下面看一个非常简单的聚合:

3.8 整体解释

一个 Actor 根据看到的 Query Model/Information,决定对External System 或者 Aggregate 执行一个 Command/Action,进而产生了某种 Domain Event。此 Domain Event 可能触发了某种 Policy,此 Policy 可能又对 External System 或者 Aggregate 执行一个 Command/Action。此 Domain Event 也可能会导致 Query Model/Information 发生变化,从而给 Actor 提供更多信息以进行其他操作。

4. 如何用事件风暴构建领域模型?

领域模型是对领域内的概念类或现实世界中对象的可视化表示。又称概念模型、领域对象模型、分析对象模型。它专注于分析问题领域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。 @百科

事件风暴的核心流程就是由用户执行了命令,从而产生了事件。基于这个事件的结果,与之前相同或是其他的用户会执行另一个命令,产生新类型的事件,以此类推。而顺序是按照业务逻辑而定的。

4.1 识别领域事件

事件风暴是一项团队活动,首先:领域专家和项目成员都聚集在大会议室里,准备好贴纸和水笔,通过头脑风暴的形式把领域中的领域事件(业务行为)都贴到墙上,形成最终的领域事件集合。

以电商为例我们得到以下领域事件集合:

PS:使用 主语+定语正方形橘黄色贴纸来描述领域事件,如“订单已创建”。

4.2 识别命令

在 4.1 中,我们已经识别出了部分领域事件,那么这些事件是谁触发的呢?触发的动作又是什么?这就是下一步动作:识别命令

命令可以理解为不同角色用户在界面上面的操作,比如“添加商品”,“编辑库存”,“提交订单”等; 有些命令可能产生多个事件,可以将他们用箭头联系起来:

PS:蓝色贴纸来标识命令,黄色贴纸来标识角色。

4.3 提取领域对象(实体)

从命令和领域事件中提取产生这些业务行为的业务对象,即实体。一个简单的做法是把领域事件中的名词都提取出来,如“商品已创建”中的“商品”、“订单已创建”中的“订单”、“库存已锁定”中的“库存”等都是领域对象

PS:黄色贴纸来标识这些实体。

实体(Entity)和值对象(Value Object)是DDD中非常重要的基础领域对象

实体(Entity):拥有唯一标识符,并且它们的标识符在历经各种状态变更后仍能保持一致,对这些对象而言,重要的不是属性,而是其延续性和标识,这种对象的延续性和标识会跨越甚至超出软件的生命周期,这样的对象就是实体对象。例如:订单 - 有作为唯一标识的订单号。

值对象(Value Object):值对象是通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体,用于描述领域的某个特定方面,并且是一个没有标识符的对象。例如:订单中的收货地址 - 有姓名、电话、省市区、详细地址等属性字段,被订单这个实体引用。

4.4 构建聚合

实体和值对象都只是个体化的业务对象,它们所表现出来的是个体的行为和能力。在领域模型中我们需要一个这样的组织,将这些紧密关联的个体对象聚集在一起,按照组织内统一的业务规则共同完成特定的业务功能,因此就有了聚合的概念。

在DDD中,聚合是一组紧密相关的领域对象,其目的是要确保业务规则在边界内的不变性,保证数据的一致性,聚合根具有全局标识,所有对聚合内对象的修改,都只能通过聚合根进行,聚合帮助我们简化了复杂的对象网络,逐步做到微服务的“高内聚,低耦合”。

从技术的角度可以这么理解,聚合是由业务和逻辑紧密关联的实体和值对象组合而成的。聚合内数据的修改必须由聚合根统一组织,以确保每次数据修改都是按照聚合内统一的业务规则来完成,聚合是数据修改和持久化的基本单元,每一个聚合对应一个仓储,实现 数据的持久化

比如订单是个聚合,它是由订单基本信息、商品信息、地址信息、发票信息等多个实体组成的,在订单聚合内每次修改商品数据时,它们都必须符合订单聚合的业务规则:“订单总金额等于所有商品明细金额之和”,违反了这个规则就会出现聚合数据不一致等诸多问题。

4.5 划定边界上下文

限界上下文(Boundary Context),是业务上下文的边界,在该边界内,当我们去交流某个业务概念时,不会产生理解和认知上的歧义(二义性),限界上下文是统一语言的重要保证。

一个聚合可能是最小颗粒度的界限上下文,同时,我们常合并业务相关性很高的聚合。

在未来的技术实现中,应当尽可能避免两个在概念上容易混淆的限界上下文内的业务需求,被同一个团队开发和维护。

4.6 梳理限界上下文依赖关系

通过分析依赖关系,提前识别依赖矛盾,减少低级设计错误的手段。每一个限界上下文都不会带有全量信息,那么补充信息的来源方向就是依赖方向,或者叫“知道(known)”的方向(我需要知道它的存在)。

操作步骤:

  1. 集体分析和讨论,并利用带箭头的实线,以依赖方向为箭头方向,绘制不同限界上下文间的依赖关系。
  2. 若出现以下依赖关系,需要思考是否存在未澄清的问题:

(1)双向依赖:上下文之间缺少一层未被澄清的上下文,或者两个上下文其实可被合为一个;

(2)循环依赖:任何一个上下文发生变更,依赖链条上的上下文均需要改变;

(3)过长的依赖:自身依赖的信息不能直接从依赖者获取到,需要通过依赖者从其依赖的上下文获取并传递,依赖链路过长,依赖链条上的任何一个上下文发生变更,其链条后的任何一个上下文均可能需要改变。

4.7 划分问题子域建立服务地图

步骤:

  1. 根据 “每一个问题子域负责解决一个有独立业务价值的业务问题” 的视角出发,可以通过疑问句的方式来澄清和分析子域需要解决的业务问题,例如“如何进行库存管理?(英文描述类似 How to…?)”。
  2. 利用虚线,将解决同一个业务问题的限界上下文以切割图像的方式划在一起,并以 “XXX子域” 的形式对每个子域进行命名。
  3. 根据三种类型的子域定义,共同结合业务实际(或者参考设计思维中的电梯演讲),确定每个子域的子域类型

PS:

  • 问题子域和限界上下文是完全不同的两个维度,问题子域解决的是问题澄清和优先级排序问题,限界上下文解决的是业务边界识别和统一语言的问题,所以在概念上其实不存在先后关系和包含关系,将两张图放在一起,是为了能够方便设和分析,可以理解为两张图的“叠加”或“映射”。
  • 对于问题子域和限界上下文的映射关系,业内存在争论(是一对多关系还是多对多关系),经过大量的实践,结合可操作性和理解上的便易性,我们刻意地选择和约定以下的映射关系:

(1)一个子域可以包含多个限界上下文

(2)一个限界上下文不应跨越多个子域 对于子域类型的判断,会随着视角的切换而有所变化,例如从全局视角识别的支撑域,从该支撑域所负责开发的团队视 角来看,该域则属于这个团队的核心域。

  • 可以先识别核心域,再识别通用域,这样的话最后剩下的就全都是支撑域。

至此,我们就完成了电商领域模型的构建了。

案例来源:事件风暴

相关文章
|
存储 SQL 关系型数据库
如何设计可落地的重构技术方案——理论篇
如何设计可落地的重构技术方案——理论篇
296 0
|
4月前
|
领域建模
领域建模问题之事件风暴的主要特点是什么
领域建模问题之事件风暴的主要特点是什么
|
4月前
|
领域建模
领域建模问题之“读模型”在事件风暴中起什么作用
领域建模问题之“读模型”在事件风暴中起什么作用
|
6月前
|
运维 前端开发 JavaScript
平台设计-概念澄清说明
平台所说模块一般指一个独立部署的前端项目
|
存储 缓存 NoSQL
概念、场景技术方案选择的理解
概念、场景技术方案选择的理解
54 0
事件风暴:领域驱动设计的实践
事件风暴:领域驱动设计的实践
用系统化思维来看今天的关窗事件
用系统化思维来看今天的关窗事件
「管理」处理复杂性-一个粗略的指南,领导模式和理论
「管理」处理复杂性-一个粗略的指南,领导模式和理论
|
Cloud Native 测试技术 数据库
【TICA解读】架构设计的本质:如何确定服务的边界
【TICA解读】架构设计的本质:如何确定服务的边界
225 0
【TICA解读】架构设计的本质:如何确定服务的边界
|
敏捷开发 消息中间件 前端开发
DDD实战之七: 战术设计、整体流程与首次冲刺
DDD实战之七: 战术设计、整体流程与首次冲刺
DDD实战之七: 战术设计、整体流程与首次冲刺