Jason's drunk words

Drunk more,talk more!

领域驱动-事件风暴

  • 佛,在信徒眼里是佛,是希望;在工艺品厂里,对于工人来说,就是一个活,是工作对象,是收入的来源;对于物流公司来说,是货,是责任担当,是运输的标的。
  • 不同的事件主题关注的业务事件是不同,领域模型也是不同的。
  • 在不同的领域模型中,统一语言。

事件风暴

事件风暴是一种快速探索复杂业务领域和对领域建模的实践。 事件风暴从领域关注的业务事件出发,经过团队的充分讨论,统一语言,最终找到领域模型。

如何确定领域关注的业务事件

在通用语言中存在“如果A发生,我们就需要做到B。”,这样的表述,那么A就可以定义成为一个领域事件。
领域事件的命名一般采用 “产生事件的对象名称+完成动作的过去形式” 的形式,这有点类似用户故事的描述。其实用户故事就可以看作是一个领域事件,只是用户故事转换成业务事件时,需要根据业务领域统一语言。

如何开展事件风暴

大部分的资料都是站在全局的高度去做事件风暴,将整个系统一起进行事件拆分,划分领域模型。
这样做当然没错,但是实际开发过程中,我们往往1:缺少领域专家;2:缺少足够是时间来做领域分析,事件风暴。这往往导致事件风暴成为理论,而缺少实践。
从我个人观点来看,架构是不断演进的,业务一直在变化,代码也一直在修改,那么,我们就可以从业务点出发,从一个需求出发,做事件风暴。

准备工作

必不可少的便利贴,开放的空间,大白板。
全员参与,包括业务,产品,开发,测试,UI。

核心概念

事件风暴将系统拆分为不同的元素,用不同颜色的便利贴表示。

统一语言

统一语言非常重要,是沟通的起点,如果一个业务内,包括业务方,产品,开发之间对于概念的表述不统一,会造成沟通不顺畅,甚至出现南辕北辙的现象。由于前期我们是从单个需求出发,可能统一语言定义出来的概念并不准确,或者在命名上有争议,不必介意,统一语言除了准确描述业务对象以外,更主要的功能是上下文的沟通和传递,只要上下文是统一的,业务就可以顺利开展,代码也可以准确编写。如果后续其他需求增加变更,发现之前定义的名称不准确,概念上修改过来就可以了。

事件风暴过程


识别领域事件

事件风暴以识别领域事件开始。书写领域事件的规则是使用被动语态,按照事件发展顺序贴在白板上。
遇到有争议的事件,不必过多纠结,先标记成热点事件,后续可以重点讨论。
事件一般由名次和动词组合而成,例如:订单已创建;地址已填写。

注意:用户的前端操作不是事件,例如:用户提交订单,用户提交表单;这些只是为事件提供数据。

识别参与者

事件一共有四种参与者:

- 角色:触发事件的人
- 策略:触发事件的规则
- 外部系统
- 事件:即当前事件的前置事件

注意:策略是规则,但规则不是策略。策略是规则+定时器的组合。策略会触发事件,但规则不会。

识别限界上下文

从两个方向识别限界上下文:

  • 纵向:识别事件流中的事件,倘若相邻两个事件的关系较弱,或者体现了两个非常明显的阶段,就可以对其进行分割。
  • 横向:梳理是有的事件,根据组成事件的名词和动词去发现事件之间的相关性(相同、相似的名词),然后去提炼一个整体的概念。

限界上下文包含场景,角色,活动,知识和能力,不包含UI部分。
限界上下文可以由不连续的事件组成。
限界上下文在命名的时候使用名词来定义。

识别限界上下文遵循的原则
  • 单一抽象层次原则 每个限界上下文从概念上应该尽量处于统一抽象层次,不能嵌套。

  • 正交原则 限界上下文之间不能互相影响,互相包含。

识别上下文映射

通过事件风暴: * 首先识别跨界限界上下文之间相邻事件的关系。 * 事件之间是否存在直接触发的关系(参与者为前置事件),需要确定这两个事件所述的限界上下文。 * 判断这两个事件所属的限界上下文,谁是主要的。主要的上下文就是下游。通常,前置事件为下游,或是事件的发布者。

下游调用上游。
事件依赖关系为单向依赖.
避免下游使用上游的的领域模型(尊奉者模式),由上游来定义参数上和返回值,下游根据情况来决定是否需要定义防腐层。
一般来说,事件如果由自己的角色参与者(角色,策略,外部系统),就与前置事件脱离来关系。

领域分析建模

一个事件只能有一个写模型,如果出现多个写模型,要么就是这几个写模型存在包含关系,要么就是写模型遗漏了对应的事件。
对于读模型,要注意它属于那个限界上下文,如果不是当前上下文,则:

  • 定义自己的读模型,通过防腐层进行转换,尽量不要迎合下游
  • 使用ID值对象(用于建立关联)(基本类型偏执)
  • 读模型和写模型就是领域模型对象
识别聚合
  • 针对领域分享模型,梳理模型对象之间的关系(继承,合成,聚合,依赖,无关系)
  • 确定领域模型对象是实体还是值对象
  • 将具有继承或合成关系的领域模型对象放在一个聚合边界内
  • 根据聚合的本质(概念完整性,概念独立性,不变量Invariant,事务一致性)梳理聚合

代码实现

角色构造型


DomainService来协调单个领域模型/值对象无法完成的业务功能,主要是数据持久化,外部接口调用获取数据等
AppService则负责业务编排
Factory负责封装复杂的创建逻辑,用于创建领域对象