事件溯源很难 - Blogomatano


我将直接告诉你:事件溯源Event Sourcing实际上有缺点的,如果您在互联网上阅读了有关该主题的任何内容,这句话肯定会让您感到震惊。毕竟,它通常是给人一种阳光和彩虹的美好感觉,当你遇到了什么问题时,都可以通过事件溯源来解决的。你生活中的大部分烦恼可能都是由于缺乏事件溯源造成的。
您已被互联网诱惑,也许意味只要开始事件溯源之旅就能过上美好的生活。好吧,在你这样做之前,我揭露真相,告诉你走上这个旅程实际上并不充满纯粹喜悦的,而是一个装满地雷的袋子,炸伤你的腿然后把你丢在充满痛苦的残缺的生活中。
我为什么要这样说呢?因为以前我是一个有能力进行设计,让团队走上了从头开始构建事件源系统的道路。在经历了复杂的应用程序部署之后,我获得了大量的伤疤、瘀伤和经验教训,以下是我伴随着事件溯源发展过程的意见,意外障碍,错误假设,不良理解。

前言
要明确的是,这不是说“你永远不应该是事件源”,或者“事件溯源是有史以来最糟糕的事情”,这只是在将事件溯源动力系统引入时出现的意外成本和生产中出现问题的集合。其中大部分可能属于“他显然不理解X”,或“你永远不应该做Y!” 在这种情况下,你是绝对正确的。重点是,在我走过“玩具”阶段之前,我不明白其中缺点或痛点。

Event Sourcing的核心卖点主要是反模式
大型事件溯源是指任何感兴趣的子系统都可以订阅事件流并愉快地侦听事件然后完成其工作。这张图片几乎可以在任何事件溯源介绍中找到:

在实践中,这可以在某种程度上同时兼具极端耦合但极其不透明。保持多个服务可以订阅和发布的中央日志的想法是疯狂的。在不进行事件处理时,您不会让两个单独的服务直接进入彼此的数据存储- 您需要通过一个抽象层来抽取它们,以避免在需要更改其数据时破坏您服务的每个消费者 - 但是,事件日志的情况并非如此:它是“直接到达并抓住那些原始数据事件”,它们是不可改变的“事实”,不变的东西不会改变,对吧?
实际上,原始事件流的订阅会破坏本地逻辑推理服务边界的能力。在“正常”开发流程下,您可以在安全,舒适的小边界内操作,构成您的服务。您可以自由选择实施和存储,然后,当您准备好时,需要处理这些事情如何暴露给外部世界,这是“服务”的核心优势之一。但是,当人们进入您的数据存储并直接阅读您的事件时,该“黑匣子”属性就会消失。协调不能在之后进行,您必须与将要使用您生成的事件的消费者进行互动,以确保事件包含足够的数据供消费者做出决策。
如果您通过上述障碍进行战斗并通过事件流成功连接一组服务,您将获得一个新问题:不透明度。多个系统读取事件流导致了一个协调层,处理怎么把这些系统连接到一起,最终将完全莫名其妙。你基本上得到了Observer重量代码所带来的所有问题,而且是在系统级别上。控制变得颠倒,导致很难推断数据如何实际流过系统,或者哪些系统消耗/产生事件,或者关心它们是否被添加/删除/修改等等。
现在,公平地说,Greg Young有一个讲话,他提到了这些问题并提倡通过流程管理器或简单的基于Actor的设计来解决它们,即引入可以作为可以路由事件的中心协调点的东西。但是,直到很久以后我才看到这种说法。我在想,分类帐会统治世界的,只好慢慢地发现通过痛苦地失败才发现这点。

新贵成本很高
当你是一个绿色的现场应用程序时,事件溯源不是一种“ 快速移动和容易破碎 ”的系统。这更像是一个“ 让我们慢慢移动,尽量不死 ”的系统。首先,您可能会从头开始构建核心组件,该领域的框架往往是重量级,过于规范,并且在技术堆栈方面缺乏灵活性,如果你想在公司环境中使用今天可用的技术并在运行中获得一些东西,那么不断发展你自己的方法就是可行的方法(以及建议的方法!)。
虽然这条道路充满乐趣,这也是超级耗时的,在计划如何在可用的基础架构上部署内容,如何确保流的行为,消息处理,如何重试失败,然后你必须实际地执行它,然后进行冲刺时又会放弃你的选择,用你新获得的知识再次实现它,直到你最终有一个足够坚实的基础,你可以真正开始构建有问题的应用程序了。
一旦你进入实施阶段,你就会意识到其他的事情:所涉及的管道代码的剪切量是惊人的。你现在已经得到了命令,命令处理程序,命令验证,事件,聚集类,并且那么你的预测,这些模型类,它们的访问类,自定义物化代码,等等。从零到工作基线需要大量的脚手架。现在,不可否认,这种伤害有多依赖于语言,但如果你是在使用一种像Java这样冗长的语言(就像我一样),你的手指会在每天结束时都会感到疲惫。
作为“入门”方面的最后一点,涉及一定的人力/政治成本。在哲学上获得整个开发团队支持是非常重要的,然而,分歧仍然会增加的。这些团队问题可能会在您的直接开发组之外蔓延。让涉及用户体验的第三方成员面临挑战。这导致意想不到的......

事件溯源需要UI方面发挥作用
这一点,虽然回想起来很明显,让我感到意外。如果你有一个UI,它通常需要与后端的事件驱动方面一起玩。意思是,它应该是基于任务的。但是,大多数常见的UI迭代都不是这样设计的。它们是静态的,基于表单的。这意味着你最终会在需要小型语义事件的后端和前端之间出现大规模的阻抗不匹配,前端会给你丰富的表单数据。
一个常见的反应可能是应用程序的那些重型表单部分可能根本不应该写入分类帐 :CRUD就是CRUD(banq注:桥归桥 路归路,表单数据不应进入事件源系统)。这是一个有趣的论点。

您潜在地在并排构建两个完全不同的系统
ES世界中一个非常常见的建议是,你不会到处都有事件来源 。这在概念层面上都很好,但实际上通过系统确定在何处以及何时绘制这些架构边界是非常困难的。
核心原因是,首先导致您进入事件溯源的需求通常不会因为您的应用程序的某些部分很“粗暴”而消失。比如如果您仍需要审核数据,是否为那些非事件驱动的部件构建了完全不同的审核策略?或者只是重用已经部署和测试过的分类帐设置?与其他系统通信怎么样?您是否构建了新的通信渠道,或重用已经存在的流式架构?
没有明确的答案,因为没有理想的路径。每个人都有自己的痛点和退缩。

审计日志中的以前状态通常会出现保真度问题
软件变更,需求变更,重点转移。那些不可改变的“事实”,以及你处理它们的能力,不会像你期望的那样持续。
如何处理不相关/错误/过时的事件?
您是否将现已弃用的事件保留在分类帐中,但在实现过程中将其“强制转换”为新事件(或no-ops),还是重写分类帐本身以删除/转换旧事件?这个领域的最佳实践经常被争论。
无论你采取哪种方式,一旦你采取它,你就失去了在重写时准确生成系统状态的能力。

因此,经常出售“100%准确的审核日志”“简单的时间查询”的想法一旦你超越了概念/玩具舞台并进入现实世界,最终会遭遇“不”的情况。如果您已将您的魔法日志创意出售给利益相关者,那么随着时间的推移,这种保真度损失可能会出现问题,具体取决于您的领域。

审计日志通常过于繁琐,无法直接使用
对应用程序中的每个操作都有一个完整的低级审计日志通常更多是一个障碍而不是帮助,意思是,大部分事件都是纯粹的噪音(banq注:领域事件与事件溯源的事件应该同一,除此以外的事件是噪音,需要过滤),实际上需要由最终用户和消费子系统过滤掉。如果您要向最终用户显示审核日志,那么离散逻辑状态通常比瞬态中间体更有价值。因此,“免费审计日志”实际上变成了“乏味的投影写入”。对于下游系统,这种混乱导致类似的协调问题。“我应该什么时候开始跑?” 并且“我应该关心事件X吗?” 在设计会议期间是一个常见问题。

作为调试工具的审计日志被认为:过度炒作
基于分类帐的另一个受欢迎的好处是它有助于调试。“如果你在应用程序中发现了一个错误,你可以重播日志,看看你是如何进入该状态的!” 我还没看到这个戏剧。99%的“坏状态”是由您的标准普通人为错误引起的不良事件。在使用标准数据库集时,使用分类帐对正常调试直觉几乎没有任何价值。意思是,如果某个age字段已损坏,您可能知道要开始调查的是哪段代码。

预测实际上并不是免费的
“你不再局限于单一的桌面结构”,Event Sourcing说。如果您需要不同的数据视图,只需以新的方式实现事件日志。“它是如此容易!”
实际上,这在初始开发成本和持续维护方面都很昂贵。您添加的第一个额外投影会使触及事件流的代码量增加一倍。可能性很大,你会写出不止一个投影。所以现在你有N个东西处理这个事件流。如果您添加,修改或删除事件类型,您就可以将该更改的知识传播到N个不同的位置。

你将处理物化视图滞后
一旦您的数据增长到无法在合理的时间内从分类帐中实现的程度,您将被迫将读取卸载到物化预测。通过这一步骤实现了物化视图滞后和读写后一致性的损失。
信息现在要么过时,要么丢失,要么就是错误。新创建的数据将为404,删除的项目将笨拙地粘贴,重复的项目将被返回,基本上所有的乐趣都是最终的一致性。
单独来说,它们并不是一件大事,但这些仍然是你必须花时间解决的问题。你是否采用后备策略进行读取?您是否花时间将智能添加到物化本身以使其更快?您是否编写逻辑以允许调用者请求他们想要的读取类型(即,以延迟为代价的分类帐,或以一致性为代价预测)?
有很多方法可以解决它。但必须解决它是我在这里得到的关键。这是需要计算,计划,实施和部署的时间(所有这些都是您应该解决的问题!)。

最后:在你超过玩具水平之前,你不会真正了解痛点。
这只是维护任何长期存在的软件的现实。无论你多少尝试准备,你做了多少背景阅读,或者你建立了多少原型,你都在做一些全新的事情。导致最痛苦的问题不会在小型测试程序中体现出来。只有你有一个活生生呼吸的机器,依赖你的用户,你无法抛弃的消费者,以及困扰软件项目的所有其他现实世界的复杂性都体现在事件溯源中。一旦你碰到他们,你就独自一人(孤独面对挑战)。

所以现在怎么办?
事件溯源不是所有都不行,我对它的抱怨就是:它已经疯狂到作为治愈的所有问题的银弹,很少有任何负面的副作用报道。我仍然非常喜欢来自事件溯源的想法,只是把它付诸实践会带来比我原本想要的更多的痛苦。  

我想你通常可以用一些单独的时间,深刻的反省和两个问题回答它:

  1. 哪个核心问题是事件溯源解决方案? 
  2. 你真正想要的只是一个普通的旧队列吗? 

如果你不能具体地回答第一个问题,如果你只是需要“审计”,“灵活性”,或“读取分离”:就不必使用ES。这些不是通过ES专门解决的问题。

一个好的历史明细表可以获得80%的分类账价值,基本上还没有任何成本。

同样,CQRS不需要事件源。您可以拥有不同投影的所有功能,而无需将分类帐放在系统的核心位置。

后一个问题是清除像我这样的迷茫的人,他们认为分类帐会统治世界。查看系统的交互点,如果您要进行全面的事件溯源,就要研究实际上会产生哪些事件?那些下游系统是否关心那些中间状态与事件,还是需要过滤的一些不必要的事件噪声?如果最终目标只是通过某种方式进行通信的分离过程,则不需要事件源。在这两个坏男孩之间放个队列就行,那就可以开始享受美好生活。