DDD 领域驱动设计学习(三)

领域事件(Domain Event) 在Eric的《领域驱动设计》中并没有提到领域事件,领域事件是在后来才被正式提出来的,并成为DDD通用语言(UL)的正式组成

领域事件(Domain Event)

在Eric的《领域驱动设计》中并没有提到领域事件,领域事件是在后来才被正式提出来的,并成为DDD通用语言(UL)的正式组成部分。领域事件(DE)是什么?领域事件的作用又是什么?
介绍领域事件的书籍和文章也比较多了,本文最后也推荐了几篇很好的文章。写这篇文章更希望多思考一下自己的一些疑问,一个是为什么要用领域事件?领域事件和一般事件驱动的区别是什么?顺便也想理一下Event相关的几个概念和工具。

领域事件是什么

在《实现领域驱动设计》一书中对领域事件的定义如下:

领域专家所关心的发生在领域中的一些事件。
将领域中所发生的活动建模成一系列的离散事件。每个事件都用领域对象来表示...领域事件是领域模型的组成部分,表示领域中所发生的事情。

Martin Fowler对领域事件的定义是:“重要的事件肯定会在系统其它地方引起反应,因此理解为什么会有这些反应同样也很重要。”

一个领域事件可以理解为是发生在一个特定领域中的事件,是你希望在同一个领域中其他部分知道并产生后续动作的事件。但是并不是所有发生过的事情都可以成为领域事件。一个领域事件必须对业务有价值,有助于形成完整的业务闭环,也即一个领域事件将导致进一步的业务操作。

领域事件可以是业务流程的一个步骤,例如订单提交,客户付费100元,订单完工等。领域事件也可以是定时发生的事情,例如每晚对账完成。或者是一个事件发生后引发的后续动作,例如客户输错密码三次后发生锁定账户的事件。

如果在通用语言中存在“当a发生时,我们就需要做到b。”这样的描述,则表明a可以定义成一个领域事件。领域事件的命名一般也就是“产生事件的对象名称+完成的动作的过去式”的形式,比如:订单已经发货的事件(OrderDispatchedEvent)、订单已被收货和确认的事件(OrderConfirmedEvent)等。

为什么需要领域事件

领域事件也是一种基于事件的架构(EDA)。事件架构的好处可以把处理的流程解耦,实现系统可扩展性,提高主业务流程的内聚性。
举例而言:用户提交一个订单,系统在完成订单保存后,可能还需要发送一个通知,另外可以产生一系列的后台服务的活动。如果把这一系列的动作放入一个处理过程中,会产生几个的明显问题:
一个是订单提交的的事务比较长,性能会有问题,甚至在极端情况下容易引发数据库的严重故障;另外订单提交的服务内聚性差,可维护性差,在业务流程发生变更时候,需要频繁修改主程序。
如果改为事件驱动模式,把订单提交后触发一个事件,在订单保存后,触发订单提交事件。通知和后续的各种服务动作可以通过订阅这个事件,在自己的实现空间内实现对应的逻辑,这样就把订单提交和后续其他非主要活动从订单提交业务中剥离,实现了订单提交业务高内聚和低耦合性。
领域事件也继承了事件的作用,当领域中发生了一些活动后,通过领域事件可以把这些活动所产生的副作用显式而不是隐式的表达出来,这种影响可以影响一个或多个聚合对象,而且可以获得更好的扩展性,对数据的锁影响也更小。

领域事件的特点

既然领域事件是一种事件,领域事件独特之处又在哪里?
个人的理解:

  1. 首先是解决领域的聚合性问题。DDD中的聚合有一个原则是,在单个事务中,只允许对一个聚合对象进行修改,由此产生的其他改变必须在单独的事务中完成。如果一个业务跨多个聚合对象,领域事件会是一个不错的工具来解决这个问题。通过领域事件的方式可以达到各个组件之间的数据一致性,通过最终一致性取代事务一致性。
  2. 其次领域事件也是一种领域分析的工具,有时从领域专家的话中,我们看不出领域事件的迹象,但是业务需求依然有可能需要领域事件。动态流的事件模型加上结合DDD的聚合实体状态和BC,可以有效进行领域建模。


     
    image.png

领域事件可以通过观察者模式和订阅模式进行实现。比较常见的实现方式是事件总线(Event Bus)。

事件风暴(Event Storming)

 

 

事件风暴也被称为事件建模,形式有点类似于头脑风暴的方法,通过事件风暴的方法可以快速分析复杂业务领域,完成领域建模的目标。
 
事件风暴

DDD提供了一套面向对象的“方言”,给出了一套面向对象的分类框架和架构指引,但是在DDD中并没有明确给出如何为一个系统识别出这些不同种类的对象的过程和方法。

而EventStorming的出现正好弥补了这个空白,通过EventStorming工作坊的方式,正好给我们提供了一个还原和分析系统的方法,并最终通过“聚合”这条红线,穿越时空,无缝切入到DDD的领域范畴之内,以“聚合”为支点,向上可以进一步做问题域和限界上下文的战略分析,向下则可以通过聚合的进一步展开进行实体、值对象等相关的战术分析,引导落地。


 
image.png

事件风暴是一项团队活动,旨在通过领域事件识别出聚合根,进而划分微服务的限界上下文。在活动中,团队先通过头脑风暴的形式罗列出领域中所有的领域事件,整合之后形成最终的领域事件集合,然后对于每一个事件,标注出导致该事件的命令(Command),再然后为每个事件标注出命令发起方的角色,命令可以是用户发起,也可以是第三方系统调用或者是定时器触发等。最后对事件进行分类整理出聚合根以及限界上下文。

事件风暴建模是一种基于领域驱动设计DDD的业务建模方式,它能够直接分析动态业务流程,克服以往静态结构分析方法的局限性。
所谓静态结构分析方法,主要表现为使用数据表结构来表达业务需求,虽然也有使用UML等面向对象的分析设计方法,但人们的分析思维还是拘泥于静态类图分析,例如DDD分析结果主要以产出类图为主、顺序图或状态图等动态分析往往被忽视。
事件建模还是一种全新的动态思维方式,代表思维方向的转变,是由“静”到“动”的转变。

事件风暴方法通常有以下几个步骤:

  1. 邀请合适的人参加
  2. 提供无限制的建模空间
  3. 发掘领域事件,可使用橙色贴纸标识
  4. 发掘领域事件的来源,探询命令,可使用蓝色贴纸标识。多种来源也可以考虑用不同颜色来区分,例如用黄色表示角色,粉色表示外部系统,红色表示时间触发。
  5. 寻找聚合,可用绿色贴纸标识
  6. 持续探索,发掘子域,BC,用户角色,验收测试标准,补充信息等。

 

 

事件风暴的步骤可以如下图所示:
 
事件风暴步骤

举个栗子

在我们的一次产品的重构活动中也采用了事件风暴方法。系统代码维护了10几年,代码中存在大量的“坏味道”:重复代码,过长函数,过大的类,过长的参数列表,发散式变化,霰弹式修改,镀金问题,注释不清等问题。实际研发过程中也是经常出现一点改动都可能会引起不可预测的结果,重构势在必行。

但是在重构过程中,也没有人可以说清楚现有系统的逻辑,如何重构成为了一个难题。重构过程我们引入了咨询公司给我们的方法,采用了事件风暴的办法,通过对领域中所发生的事情(也就是领域事件)来探索这个领域,并且使用便签来描述领域中的事件,这些便签会沿着时间轴贴到一个很大的建模面板上。

举例来说,能够引发事件的事情包括用户行为、外部系统所发生的事情以及时间的流逝。事件也有助于找到领域的边界,对术语的不同阐述可能就意味着存在边界。

  1. 准备工作,四色贴纸:
    橙色:事件,某个动作的结果,以“XX已XX”的方式表示,比如“用户信息已查询”
    蓝色:属性,事件相关的输入、输出数据等
    黄色:命令,某个动作,比如“查找用户信息”
    绿色:实体,命令的触发者
  2. 开始梳理业务,将结果贴到白版上
  3. 继续深入梳理,将整个过程的模型、关键数据等梳理出来,贴在白板上
  4. 确定重构指导思路,执行重构动作,重构的同时引入单元测试保障重构的质量


作者:数行者
链接:https://www.jianshu.com/p/acb7c8026c21
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。