言之无物

我说的一切都是错的

DDD事件风暴(上)——原理篇

原文链接:https://developer.ibm.com/technologies/java/tutorials/reactive-in-practice-1/#

前言

范钢老师在devops社区的系列分享第一篇-《快速交付》之设计篇,以电商中的购物环节为例,利用领域驱动设计方法来建模,介绍了事件风暴,聚合,聚合根,命令等一系列概念。作为DDD的入门者,这些概念还是比较模糊的。在IBM Developer找了一篇文章,详细介绍了DDD的基本概念和实践,翻译出来与大家分享。

以我个人的经验,很少能看完公众号上长篇的文章,基本翻几页就放弃了,或者先收藏着,然后从来也不会去打开收藏。所以还是分两次发布,上篇以股票领域为背景,介绍DDD的基本概念,下篇介绍如何在股票交易领域实践DDD。

等不及的朋友可以直接跳转到原文链接。

为什么采用事件风暴和领域驱动设计?

事件风暴和领域驱动设计的目标是创建一种技术无关的语言,达成对业务需求和流程的详细了解。让业务领域专家——这些最熟悉股票交易领域和业务角色的人——能够和团队的其他人分享领域知识。建模工作坊需要参与的干系人,可能包括技术专家、项目管理人员、用户体验专家、质量保证分析师,以及任何在项目运作中涉及的人;当然,业务领域专家最重要的。

同步系统依赖于请求/响应式语义;比如,我们调用一个方法或者Restful端点,并期待得到响应。 调用者收到响应前会一直等待。 如果响应延迟或失败了,会由于超时无法获取资源。来自第三方集成系统任何的延迟,最起码会导致服务明显的时延。

与之相反,我们将演示一个异步的,没有阻塞的系统开发。为此,需要一套能够适应这种设计和开发风格的建模方法。

事件风暴和DDD允许我们以异步的方式建模,非常适合响应式系统和云原生系统的分析和设计。

事件风暴介绍

事件风暴召集项目所有的干系人,让大家对业务领域和身边的问题达成一致的理解,这种理解无关乎技术。解决方案建立在合适的业务上下文基础上,确保领域专家和技术专家在构建系统前达成一致的理解。

事件风暴是一个高度协作的流程,经常能发现相当多的知识点。如果不采用这种方法,它们只会藏个人和团队心中。

在事件风暴中,我们用橘黄色便签代表事件。起步很容易:干系人想一想,在桔色便签上写下感兴趣的业务事件,把便签粘在某个固体表面上(比如墙上的空白纸,事后可以方便的卷起来)。在开始的10-10分钟时间里,或许有一些有趣的事件被识别出来。一旦我们有足够的事件,就需要创建流:事件流就是根据时间顺序从左到右排列的事件集合。

事件风暴工作坊是一个非常动态的体验, 我们可能从一个非常简单的事件流开始。随着一些事件被发现,必然会伴随着更细致的讨论。和UML等较正式的建模技术不同,沟通和讨论是这个练习最关键的输出。这并不是说,工作坊不能输出图表或其他的构件,主要是不希望这么快把有价值的对话变成固定的表示。事件风暴本身就是自由的沟通讨论。

领域驱动设计介绍

请注意,我们不仅仅识别事件,也开始发现特定业务上下文中的一些术语。在股票交易领域,我们将使用术语“股票”, “报价”,订单”,它们将变成业务通用语言。保持一致性有利于交流。

领域驱动设计给了我们一个模板,可以把事件风暴的输出转变成模型,足够正式,可以用于架构和构建真实世界的系统

在继续之前,我们假设读者已经理解了这些预备知识。

准备开始:基础

我们会走一遍建模流程,了解股票交易业务领域,把业务专家作为知识源。然后,使用合适的领域语言,把领域概念转变成代码中的领域逻辑。这会让技术专家和业务专家在整个设计和实现中始终保持一致的理解。

经过这个初步的设计流程,我们利用事件风暴做到:
1. 对股票交易系统的事件和命令流建模。
2. 把命令和事件归到不同的子域(subdomain),可以给不同的团队去实现。
3. 在子域之间创建接口

事件

先从识别重要的事件开始。 事件是对已发生事实的陈述(比如,股票已购买)。换句话说,一个事件集是指系统中已经发生的一些历史记录。事件一般用过去时态来表达。

理论上, 无论有多复杂,我们都可以对整个业务建模,形成一个事件流。 不过,实际上,不太可能把一个大公司中所有的干系人都请到房间,期待取得任何有意义的进展。所以,我们得把事件风暴限制在一个在特定的业务领域——不是技术领域——然后进一步把流程分解到各个领域和子领域中。

一般来说,事件风暴会议启动后,参与者都开始往墙上贴事件——任何有趣的,值得讨论的事件。在股票交易领域,有趣的事件包括:
– 客户打开了新的投资组合。
– 客户购买了投资组合中的一些股份。
– 客户卖出了投资组合中的一些股份。
– 客户关闭了投资组合。

当我们继续考虑新的事件时,可能会注意到,事件风暴会议产生了一堆无序的事件。当有比较完整的事件流显现时,我们需要把这些事件排成一个线性的顺序。这有助于发现业务流程中的差距。

比如, 我们创建一个序列“客户打开了一个投资组合”,“客户购买了一些股份”。它会提醒我们思考,“客户用什么买的?” ,由此,我们会在两个事件之间增加一个新的领域事件“客户往投资组合中增加了现金”。
《DDD事件风暴(上)——原理篇》

我们通常从事件开始。事件是一个系统天然的基础,因为它们是业务流程最贴切的表示法,能围绕它们和主题专家展开讨论。另外的构建块,比如命令,帮助我们详细阐述流程,构造出异步系统的内在模式。

事件风暴是一个迭代式的流程,可能需要多个迭代来讨论事件和命令,才能形成一个基于业务需求的,比较准确的流。

命令

命令通常是一个或多个事件序列的触发器,用蓝色便签来记录。事件代表着过去不可改变的事实,而命令用来表达让事情在未来发生的意图。

命令可以被驳回。比如,由于客户投资组合中没有足够的股份,导致“卖出股票”这个命令被驳回,

命令通常用现在时态来表示,而事件总是过去时态。
《DDD事件风暴(上)——原理篇》

聚合

我们可以轻易的识别一些聚合,比如一个投资组合。

一个投资组合可能包括一些现金和各种类型股票。 一个投资组合也是一个实体——也就是,它具备固有的识别特性——因此,即使你的投资组合和我的组合有同样数目的钱和股票,它们彼此仍是不同的。我往组合里增加一些股份,它还是原来那个组合,即使里面的内容不一样了。

实体,聚合和聚合根

继续之前,我们对实体,聚合和聚合根这些术语再做些补充。

实体,聚合和聚合根之间的区别很容易搞混。 就像我们总是搞不清java中的对象图和对象图根对象的关系一样。

举个例子,一个链接列表可以看成是彼此连接的节点集合,也可看成是指向列表中首节点的引用。如果我们取得了首节点的引用,我们会毫不犹豫的说我们获取了整个“列表”的引用。

如果这个列表表示工作项队列,通过节点之间的引用可以更新工作队列。我们会把工作队列当成一个实体, 随着工作的进行,它的状态会改变。

在这个例子中,当我们谈及“工作队列”时,我们是指列表头节点的引用(实体),列表的头节点(聚合根),还是列表中元素的有序集合(聚合)呢?

在实践中,我们经常忽略这些区别。在事件风暴会议中,保持一定水平的细节程度是必要的,我们主要关心的是基于业务上下文的描述。明白了这一点,只要确保对话是彼此可理解的,同时有非技术的干系人参与,这个细节程度就是合适的。在后续的技术方案练习中,会有更多的时间来弄清它们之间的区别。

聚合建模

我们用淡黄色便签表示聚合。聚合代表系统中有状态的实体。比如,一个投资组合是聚合,一个银行账户也是。

聚合都是与状态相关的,那就还得有改变状态的方法。命令指向聚合,这是改变聚合状态的唯一办法。有一点要特别注意,命令不会强制去改变状态。当聚合接收到命令时,可以选择改变状态,或者完全忽略它。

一旦聚合决定改变状态,会发出一个表示状态变更的事件。比如, “出售股份”命令作用到“投资组合”,会产生一个事件“股份已售出”,同时组合中的股份数会减少。投资组合的状态变了,而且“股份已售出”这个事件也会发布给订阅者。

如果状态无法变更,也可以发出事件。比如,由于股份不足无法完成交易,会发出一个“交易已拒绝”的事件。

改变实体状态只能通过事件。要重建实体的状态,我们可以从头开始,把聚合范围内(也可认为是事务边界)曾经发生过的事件重新触发一遍。 这是事件溯源的核心概念,在后面的系列中会深入探讨。

在事件风暴中,要描述清楚所有的状态变更,事件本身要有足够的“描述性”。事件的描述命名是捕获状态变更背后相关业务逻辑的关键,也是捕获命令所有可能输出的关键。

至此,我们可以看出一个通用的模式:
1. 一个命令表达想改变实体状态的意愿(比如,卖出股份)
2. 一个事件表示预期的动作已经完成(比如,股份已卖出)
3. 实体状态基于事件来更新
4. 事件会广播给各个订阅者去消费
《DDD事件风暴(上)——原理篇》

角色(Actors)和响应(Reactions)

事件可以由用户执行操作触发,也可由系统内部命令触发。

事件通常是由用户操作触发的。用户在事件风暴中称为角色(actor)(不要和Akka框架中actors混淆),用小的黄色便签,粘在命令上。 之所以采用角色标记法,是为了在后面的建模过程中方便计划和估算;UI和UX团队会自动关注与角色相关的所有命令,能够轻易的界定工作范围。

响应(也被称为策略-Policies)用来给触发事件的系统内部命令建模。比如,当符合某一条件时,就启动一个命令。这时,采用“当…时,就…..”标注法来建模就很方便。按照惯例,响应用淡紫色的便签表示。当事件对应多个命令时,可以在命令和事件之间插入策略。或者直接把策略附在命令上,这时,一个策略只对应一个命令。

《DDD事件风暴(上)——原理篇》
上图是电商中一个购物车的流图。我们运用角色和策略,画了一个完整的业务需求图。可以看到,用户可以把商品加入购物车,当取消购物车时,会发出一封跟进邮件。

事件风暴总结

在建模的初始阶段,协作很重要,同时也是灵活和快节奏的。事件风暴给我们提供了一种完整,简单而又不乏表达力的建模语言。以上介绍的关于事件风暴最小集词汇,让你能在建模会议中开始运用这项技术。

在事件风暴工作坊中,我们强烈建议在显眼的位置贴上图例和样例流图,供团队参考。
《DDD事件风暴(上)——原理篇》

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注