DDD实战:使用事件风暴和域驱动设计对反应式系统进行建模
一个成功的事件风暴会议——以及一个成功的软件项目——需要同等的艺术、知识和技术技能。此外,对便笺进行更改比对生产代码进行更改要便宜得多。通过编写代码来了解您的系统是理解和完善所涉及的业务流程的一种非常昂贵的方法。
本文中的任何内容都不需要技术专业知识或以前使用反应式事件驱动系统的经验。无论您是业务专家、开发人员、架构师、产品所有者还是企业主,您都应该学习一些新技术,这些技术将帮助您与团队和组织协作获得更多成功和乐趣。
什么是反应式系统?
在反应式系统中,可以实时响应组织内发生的有趣业务**事件,而无需集中控制和整体系统架构的协调成本。您的软件系统更准确地反映了业务的真实本质。
虽然"反应式"在经典上被认为是一个负面术语,但在计算机系统中,它是理想的控制流。系统不需要显式更改系统的每个部分来使新事物发生,而是在事件发生时对事件做出反应。想象一下,您是否能够订阅组织内任何有趣的事件并创建新系统来对这些事件做出反应,而无需与任何其他团队协调?这就是我们的目标。
发货系统订阅 Order 事件并做出相应的反应。新系统还可以订阅订单,而无需向订单团队请求更改。
例如,如果配送系统需要来自订单系统的信息,则配送团队无需请求订单团队进行更改,他们只需订阅订单系统发出的相关事件即可。如果默认情况下发布有趣的事件,则团队需要直接相互协调以根据业务需求进行重要更改的情况很少见。
这是可能的,因为在事件驱动的系统中,域事件被发布到期刊上。组织中有权访问日记的任何其他系统都可以订阅已发布的事件并实时响应这些事件。关于这种方法最有趣的事情是它的简单性;几个世纪以来,企业一直在通过期刊运营。我们只是将这种方法带入现代语境。
存款等事件可能会引发反应,例如更新您的可用余额。
这种程度的灵活性之所以成为可能,是因为多年来技术基础设施有了很大的改进,例如网络的速度和可靠性。十年前,我们无法证明通过网络发出事件所涉及的延迟损失是合理的,因此我们优先考虑了局部性并构建了可以在单台计算机上运行的整体式应用程序。
现在,整体式系统的规模和复杂性已经增长,以至于许多团队在单个应用程序上的协调成本太大,不容忽视。基础设施的改进意味着分销费用迅速下降,因此现在复杂的企业系统正在被重新视为事件驱动系统。这对开发人员和领域专家如何看待系统设计产生了巨大影响。
整体式架构迫使团队产生巨大的协调成本。分布式架构允许团队以更大的自主性和灵活性工作。
组织现在拥有完成看似不可能的工具,例如将隔夜批处理流程转换为实时系统,将夜间生成的静态报告转换为近乎实时地显示全局各个方面的任务控制仪表板,等等。想象一下,制定策略,自动响应业务中的重要事件?它正在改变游戏规则。
现在我们所需要的只是一种现代技术来帮助我们对这样的系统进行建模。
事件风暴
事件风暴是组织人类学的一种形式;它就是讨论组织中的事件流,并以易于理解的方式对事件的流动进行建模。从事件风暴会议中获得的知识最终将融入到其他建模技术中,为出现的业务流程提供结构,但真正的价值在于所涉及的对话。您可以从模型构建软件系统,或者只是使用从对话中获得的知识来更好地理解和优化业务流程本身。
工作坊
举办活动风暴研讨会很简单:一个系统的利益相关者进入一个房间,里面有无限的建模表面——一张贴在墙上的巨纸——并在上面贴上便签。听起来很简单?是的。这就是事件风暴的乐趣;它纯粹专注于手头的任务,即理解您的业务流程,而没有阻碍对话的所有过于复杂的建模技术。
没那么快。我们需要遵循一套指导方针,这样我们就不会陷入混乱。事件风暴的价值在于沟通,而不仅仅是在墙上贴上便笺。
现在,我们将介绍在讨论和建模事件驱动型业务流程(事件、反应和命令)时涉及的核心概念。
什么是活动?
首先,我们将开始纯粹根据事件和反应来考虑我们的业务流程,这是一种说"因果关系"的花哨方式。
事情发生了。并非所有这些都很有趣,有些可能值得记录,但不会引起反应。最有趣的会引起反应。许多系统需要对有趣的事件做出反应。通常,您需要知道为什么系统会以这种方式做出反应。
—— Martin Fowler,《Domain Event》
我们可以将马丁的名言重述如下:
"重要事件会引起系统其他地方的反应,了解这些反应发生的原因通常很重要"。
我们可以将这个单一语句作为我们的指导原则,并用它来建模、构建和构建事件驱动的系统。
事件流
单个事件不是很令人兴奋,因此我们需要从流程的角度来考虑事件 - 随时间变化的事件。 橙色粘滞表示事件。为了对业务流程进行建模,我们只需按时间顺序从左到右排列事件。就是这样!听起来很简单?是的。这就是重点。你和你的团队之间没有不必要的仪式,有趣的讨论会产生结果。
一旦我们有了随时间变化的线性事件序列,我们就需要考虑这些事件的原因。
什么是反应?
反应是在其他事情发生后需要发生的事情。反应最好在以下陈述中捕获,
"每当发生这种情况时,就会发生这种情况"。
这就是反应。这就是事件。"何时"是将事件与创建策略的反应相关联的单词。
如果你发现自己在描述一系列事件时都使用这个术语,那么你就是在用事件驱动的系统来说话。这些语句的顺序不如语句的内容重要。
"每当创建新的用户帐户时,我们都会通过电子邮件向她发送确认"。
如果你读了上面的陈述,关键是时态。事件总是过去时态,而反应总是未来时态。更重要的是,我们从不讨论"现在"——我们不能对系统的当前状态做出任何假设,因为不能保证我们打算发生的事情真的会发生。错误的假设是计算机系统中大多数故障的根本原因。通过只对因果关系进行建模,我们可以更好地决定如何对任何事情做出反应,因为我们不会对结果做出错误的假设——不仅是成功的结果,还有错误和其他不太理想的事件。
什么是策略?
事件和反应一起的流称为策略 — 我们将此流称为策略,因为该流捕获了核心业务规则,例如"每当有人登录客户帐户时,我们需要提醒客户"。
单个事件可能会导致多种反应。考虑当事情出错时会发生什么也很重要。可以将整个流程视为"帐户创建策略"。
同样重要的是要注意,一个反应——或整个政策——不需要代表一个软件。例如,“确认付款详细信息"可能是一个手动过程,涉及管理员手动验证电汇详细信息。整个策略甚至可以作为手动流程规则实现,例如团队相互打电话而不是自动计算机系统。事件风暴是一种非常有价值的技术,可以了解组织中的手动流程以及设计新软件。
我们还看到,反应往往引起新的事件,例如反应的"扇出”;扇出是指两个或多个效应从导致这些效应的另一侧出现。事件驱动的系统都是关于因果关系的,因此捕获尽可能多的结果非常重要。以这种方式导致扇出的事件可能值得深入研究,甚至可能值得自己的事件风暴会议。
关注政策的另一个原因是讨论和设计流程的重要性,以便在出现问题时进行讨论和设计;例如,如果付款详细信息未成功确认,会发生什么情况?
现在我们知道事件可以引起多种反应,而这些反应可以引起新的事件。还有什么可能导致事件?
- 外部系统(粉红色粘滞剂)
- 命令(蓝色粘滞)
- 用户发起的命令(用人注释的蓝色便笺)
捕获用户输入、基于用户输入发出事件、处理付款以及与外部支付网关交互的业务流程。
在上面的流程中,我们只用了几个便利贴就捕捉到了很多细节。我们知道,用户可以输入信用卡详细信息,提交信用卡时会生成事件,并且支付处理器与外部支付系统进行交互。
一个专注于此业务流程的简单研讨会可能会产生更多的讨论,例如:
- 我们需要从用户那里捕获什么类型的信息?
- 我们在信用卡活动中包含哪些类型的信息?
- 支付处理器的细节是什么,它如何与外部系统交互?
我们还可以在流程中使用其他一些有趣的元素:
- 计时器(带时钟的橙色贴纸)
- 计划程序(带有日历的橙色便笺)
事件也可以安排和计时。讨论业务流程的所有规则非常重要,而不仅仅是用户交互。
计划程序和计时器也是非常有趣的事件类型,但它们通常是实现级别的考虑因素。在以业务为中心的会议期间,我们不需要深入研究这种详细程度。最终,开发人员将需要更高的设计精度,并将考虑实现的各个方面,包括需要安排的事件。
什么是命令?
最终,我们需要更深入地研究并准确绘制出用户如何与我们的系统进行交互;命令至关重要,因为它们通常表示用户交互。将用户交互添加到事件流中,使我们能够对系统的完整因果关系进行建模。
在上面的示例中,我们可以可视化用户提交的信用申请,并最终导致批准或拒绝决定。随着我们继续对此业务流程进行建模,我们最终将采用更详细的元素来帮助捕获:
- 用户界面要求
- 数据模型
- 等
敏锐的观察者也会注意到,命令与反应非常相似。命令和反应之间的唯一区别是,命令通常由用户发起,而反应通常由事件发起。从一开始就就如何讨论命令和反应的细微差别达成一致会对您的团队很有帮助 - 例如,一些团队纯粹使用反应来记录更复杂的过程,并将命令用于系统和用户操作。定制您的团队使用各种事件风暴元素的方式来满足您自己的需求是完全可以接受的,只要您的团队制作了每个人都遵循的传奇。
对于高级事件风暴,命令、事件和反应为我们提供了足够的构建块来讨论系统的核心。我们最终将希望更深入地了解实现领域。一旦我们到达那里,我们将需要一些元素来帮助我们建模和定义系统的结构。
在事件驱动系统中定义结构的最有效方法是使用领域驱动设计技术。
接下来,我们将介绍域驱动设计的基础知识,以及一些关键的构建块如何帮助我们在流程周围放置结构。
领域驱动设计
事件风暴会话可用于对业务流程进行建模,而域驱动设计则提供了将这些流转换为结构化系统的纪律。当我们提到系统时,我们并不自动意味着代码,我们只是意味着最纯粹意义上的"系统",无论该系统是手动的还是自动的。
在所有软件开发方法中,领域驱动设计是唯一一种专注于语言作为深入了解给定领域复杂性的关键工具的方法。
— Alberto Brandolini, Event Storming
在高层次上,领域驱动设计通过识别组织内的自然边界并确定跨这些边界所需的合作,围绕事件提供结构。
想象一下,在该场景中,您尝试对跨大型公司中跨团队甚至部门的业务流程进行建模的常见场景…
业务流程经常跨越组织边界。域驱动设计为相应地构建系统提供了指南。
团队要就跨越组织边界的流程达成一致,需要付出巨大的协调成本。当一个系统必须由多个团队维护时,这更加痛苦。这种类型的阻抗是我们必须不惜一切代价避免的。领域驱动设计为我们提供了一些工具,可以帮助我们避免这种情况,尽管没有建模技术是万无一失的。
为了将结构融入到流程中,我们将重点介绍 DDD 的两个核心元素 — 聚合和有界上下文。
什么是聚合?
聚合在逻辑上将各种命令、事件和反应组合在一起。这有助于我们以完全尊重业务流程中关系的方式制作软件。
当我们达到这种详细程度时,我们可以开始从更具体的角度思考聚合事件和域事件。
一个帐户聚合,该聚合以逻辑方式对与帐户相关的命令和事件进行分组。
聚合事件在聚合本身的边界内相关,而域事件与更广泛的系统相关。有许多事件是如此具体,以至于将它们泄漏到更广泛的组织中会导致太多的混乱,因此聚合边界本质上传达了"这些事件仅在聚合的边界内是有趣的 - 在此边界之外发出的任何事件都应该对域感兴趣"。
在更深入地了解解决方案领域之前,我们不需要创建聚合。一旦我们开始从聚合的角度来思考,我们实际上是在考虑实现——但还有另一个原因可以尽早开始考虑聚合。
人类的大脑只能在学习能力耗尽之前保留如此多的复杂性。遗留系统最有害的方面之一是在系统内生产之前所需的知识的无限性。
如果我在运输模块中更改一行代码会发生什么;它会中断付款吗?
聚合通过彼此隔离相关关注点来为我们的设计带来结构。
聚合边界内的所有功能都应易于一个人理解,包括该边界内更改的影响。
在专注于业务流程的高级建模会议期间,利用聚合并不重要,但是如果您发现自己在考虑解决方案方面,则聚合为有意义的实现讨论提供了必要的结构。
什么是有界上下文?
有界上下文用于在大型复杂系统中创建隔板。就像船体中的舱壁可以防止突破物沉没整个船一样,系统中的隔板可以防止不必要的复杂性泄漏到上下文边界之外。
付款团队负责发出付款事件。发货团队只需订阅这些事件,并实现自己的发货逻辑。
聚合边界将相关行为组合在一起,而有边界上下文则将相关的语言、意义和文化分组。例如,“发货"上下文中的"确认"可能与付款上下文中的"确认"含义完全不同。有界上下文有助于避免像"ShippingConfirmation"这样的可怕怪物逃离笼子并在整个组织中造成严重破坏。
通过定义有界上下文,我们可以开始了解系统的子域如何交互,而无需深入研究代码的内脏。Shipping系统可能是一个遗留的J2EE应用程序,支付系统可能是一个内置于Akka中的现代系统,但它们纯粹在事件中相互交谈。
结论
事件风暴是一种协作练习,它不分职称或层次结构,只有想法才是重要的 - 但是,在现实世界中,决策者需要通过行动来推动练习。这些模型是对话的副产品,而这些对话才是真正的价值所在。沟通的增加将在整个组织中产生连锁反应。模型可以用作软件开发的可操作蓝图,但它们也可以简单地用作增强通信流的工具。当蓝图用于构建软件时,结果是反映业务语言和结构的代码。然后,跨职能团队能够使用与业务相同的语言和模型来讨论和推理代码,从而确保未来的协作是有效和准确的。
- 原文作者:知识铺
- 原文链接:https://geek.zshipu.com/post/DDDxx/%E4%BD%BF%E7%94%A8%E4%BA%8B%E4%BB%B6%E9%A3%8E%E6%9A%B4%E5%92%8C%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E5%AF%B9%E5%8F%8D%E5%BA%94%E5%BC%8F%E7%B3%BB%E7%BB%9F%E8%BF%9B%E8%A1%8C%E5%BB%BA%E6%A8%A1/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com