事件驱动“Event-Driven”是什么意思?

Martin Fowler对事件驱动 EventSourcing和CQRS最近进行了权威解释:

去年年底,我与ThoughtWorks的同事们一起参加了一个研讨会,讨论“事件驱动”应用程序的性质。 在过去的几年里,我们通过使用大量的事件建立了许多系统,常被称赞,也常诅咒。我们的北美办公室组织了一次峰会,来自世界各地的ThoughtWorks高级开发人员分享了想法。

峰会的最大结果是认识到当人们谈论“事件”时,他们实际上意味着一些完全不同的事情。 所以我们花了很多时间试图从中挑出一些有用的模式。 本文就是我们确定的主要内容的简要总结。

事件通知

当系统发送事件消息以通知其他系统在其领域中进行更改时,会发生事件通知这种情况。事件通知的一个关键元素是源系统并不真正关心它发出事件的响应。通常它不会期望它发出事件的任何回应,或者如果就是有一个响应需要源系统的关注,这种关注也是间接的。在发送事件的逻辑流和响应对该事件的某些响应的任何逻辑流这两者之间将存在明显的分离。

事件通知很好,因为它意味着低级别的耦合,并且设置非常简单。然而,如果真的有一个逻辑流运行在各种事件通知,它可能会成为问题。问题是,很难看到这样的流程,因为它不会在任何程序文本中显式突出。 通常,找出此流程的唯一方法是监视实时系统。 这可能让这种流变得难以调试和修改。 危险是,很容易做出与事件通知解耦很好的系统,但是却没有意识到潜在地存在你看不见的大规模流量,这样在未来几年将自己置于麻烦之中。 该模式(事件通知)仍然非常有用,但你必须小心的陷阱。

这个陷阱的一个简单示例是当事件用作passive-aggressive命令时:源系统期望接收者执行动作时,并且应该使用命令command消息(不是事件消息)来显示该意图(表示意图应该是命令,而不是事件,事件包含的是已经执行意图进行修改后的数据 banq注),但是却将消息风格不恰当变为事件。

事件不需要携带很多数据,通常只是一些id信息和链接指向发送者,可以查询更多的信息。 接收者知道某事已经改变,可以获得关于改变的性质的一些最小信息,然后可向发送者发出请求以决定下一步做什么。

事件承载状态转移

当您想以不需要联系源系统就可以进行下一步工作的方式来更新系统的客户端时,将使用该模式。 每当客户端更改其详细信息(例如地址)时,客户管理系统可能会触发事件,其中事件包含已更改的数据的详细信息。 然后,接收者可以利用这种变化的事件通知来更新其自己的客户数据副本,使得它以后再也不需要与主客户系统进行通信就能完成它自己的工作。

这种模式的一个明显的缺点是,有很多数据被抛出和并存在大量的复制副本。但是,在空间充足的存储时代,这不是一个问题。我们获得的是更大的恢复力,因为如果客户系统变得不可用,接收系统还是可以运行。 我们减少了延迟,因为没有远程调用RPC以同步方式访问客户信息。我们不必担心客户系统上的负载是否满足来自所有客户系统的查询。 但它涉及更复杂的接收器,因为它必须整理出保持原来顺序的所有的状态,它通常在需要时更容易调用发送者以获得更多的信息。

事件源Event-Sourcing

事件源的核心思想是,每当我们改变系统的状态时,我们将状态变化记录为一个事件,我们可以通过在将来的任何时间重新处理事件来自信地重建系统状态。事件存储器成为真实业务变动的主要来源,并且系统状态是纯粹从事件导出的。 对于程序员,最好的例子是版本控制系统。所有提交的日志是事件存储,源树的工作副本是系统状态。

事件溯源引入了很多问题,我不打算在这里说太多,但我想强调一些常见的误解,事件处理不是一定需要异步,考虑更新本地git存储库的情况 -这完全是一个同步操作,因为它如subversion一样更新一个集中版本控制系统。 当然还有很多这样的提交commit能够实现各种有趣的行为,git是一个很好的例子,但核心提交从根本上是一个简单的动作。

另一个常见的错误是假设任何使用事件源系统的人都应该理解并访问事件日志以确定有用的数据。 但是事件日志的知识却相当有限。 我在一个编辑器中编写它,这个编辑器不知道源代码树中的所有提交,它只是假定磁盘上有一个文件。 事件源系统中的大多数处理应基于有用的工作副本。 只有一个组件应该知道如何从事件存储区导出该工作副本。我们可以有不同数据结构的多个工作副本,当然如果这样做有帮助的话; 但通常应该在领域处理和从事件日志导出工作副本之间有明确的区分。 使用事件日志时,构建工作副本的快照通常是有用的,这样您就不必在每次需要工作副本时都从头开始处理所有事件。

事件源具有许多有趣的优点,当考虑版本控制系统的价值时很容易想到。事件日志提供强大的审计功能(会计凭证来往事务是科目帐户余额的事件源. banq注)。 我们可以通过重放事件日志来重建历史状态。 我们可以通过在重放时注入假设事件来探索替代历史。事件源使得具有非持久性工作副本(例如内存映像)是合理的。

事件源确实有其问题。 当结果取决于与外部系统的交互时,回放事件变得有问题。我们必须弄清楚如何随时间的变化处理事件的模式。 很多人发现事件处理给应用程序增加了很多复杂性(尽管我不知道这是否是由于派生工作副本的组件和执行域处理的组件之间的分离得比较差导致的)。

CQRS

命令查询响应分离(CQRS )是具有用于读取和写入信息的单独数据结构的概念。严格地说,CQRS不是真的关于事件,因为你可以使用CQRS,而在你的设计中不存在任何事件。但通常人们将CQRS与早期模式组合在一起,因此他们在峰会就有存在的必要进行演讲。

CQRS的理由是,在复杂领域中,处理读取和写入的单个模型太复杂,我们可以通过分离模型来简化, 这在访问模式有所不同时尤其有吸引力,例如大量读取和很少的写入。但是使用CQRS的收益必须与具有单独模型的额外复杂性相平衡。 我发现很多同事对使用CQRS非常警惕,发现它经常被滥用。

了解这些模式

作为一个软件植物学家(botanist),渴望收集样品,我发现这是一个棘手的情况。核心问题是混淆不同的模式。在一个项目中,有能力和经验丰富的项目经理告诉我,事件源是一个灾难 - 任何更改花了两倍的工作来更新读和写模型。就在这篇短文中,我可以检测到事件源和CQRS之间的潜在混淆 - 那么我怎么弄清楚哪个是罪魁祸首呢?项目的技术主管声称主要的问题是许多异步通信,当然这是已知的复杂性 - 源头,但异步机制不是事件源或CQRS的必要部分。此外,我们必须注意,所有这些模式在正确的地方是好的,当放在错误的场景下就是坏的。但是,当我们将模式混合在一起时,很难弄清楚什么是正确的场景。

我想写一些确定的论文,把所有这些困惑排除在外,并给出了如何做好每个模式,以及什么时候应该使用的坚实指导。 可悲的是我没有时间去做。我写这个笔记,希望它将是有用的,但我很清楚,它远远落后于真正需要的。

What do you mean by “Event-Driven”?