为什么事件存储应该在写入端?

时间:2012-05-14 17:33:20

标签: cqrs event-sourcing

事件采购作为许多事情的奖励,例如:事件历史/审计跟踪,完整和一致的视图重新生成等。听起来很棒。我是粉丝。但这些是读取端实现细节,您可以通过将事件存储完全移动到读取端作为另一个订户来完成相同的操作..那么为什么不呢?

以下是一些想法:

  1. views / denormalizers本身并不关心事件存储。他们只处理域中的事件。
  2. 将事件存储移动到读取端仍然为您提供事件历史记录/审核
  3. 您仍然可以从活动商店重新生成视图。除了现在它不必是写模型泄漏。给他读模特公民身份!
  4. 这似乎是将其保持在写入方面的一个技术论据。这来自Greg Young http://codebetter.com/gregyoung/2010/02/20/why-use-event-sourcing/

      

    然而,使用存储当前状态快照的东西存在一些问题。最大的问题围绕着您为数据引入了两个模型这一事实。您有一个事件模型和一个表示当前状态的模型。

    我觉得有趣的是“快照”这个词,它最近也成为了事件采购中的一个杰出术语。在写入端引入事件存储会增加加载聚合的一些开销。您可以讨论多少开销,但这显然是一个感知或预期的问题,因为现在有从快照加载聚合的概念以及自快照以来的所有事件。所以现在我们又......两个模型了。不仅如此,我所看到的快照建议都是作为基础架构泄漏实现的,后台进程会遍历整个数据存储,以保持高性能。

    在拍摄快照之后,快照之前的事件从写入视角变为100%无用,除了...重建读取端!这似乎不对。

    另一个与性能相关的主题:文件存储。有时我们需要将大型二进制文件附加到实体。从概念上讲,有时这些与实体相关联,但有时它们是实体。将它们放在事件存储中意味着每次加载实体时都必须物理加载这些数据。这已经够糟糕了,但想象一下这些中的几个或几百个。我所看到的每一个答案都是基本上咬了一口子并将uri传递给文件。这是一个警察,破坏了分布式系统。

    然后是维护。重建视图需要涉及事件存储的过程。因此,现在您编写的每个视图维护任务都会将您的写入模型绑定到使用事件存储...永远。

    CQRS的重点不在于读取模型和写入模型周围的用例是否根本不兼容?那么我们为什么要把读取模型的东西放在写入端,牺牲灵活性和性能,并再次将它们重新耦合。为什么要花时间?

    总而言之,我很困惑。在我所处的所有方面,事件存储作为阅读模型细节更有意义。您仍然可以实现保留事件存储的诸多好处,但是您不会过度抽象写入持久性,可能会降低灵活性和性能。并且您不会通过漏洞抽象和维护任务来重新配置您的读/写端。

    那么有人可以向我解释一个或多个令人信服的理由将其保留在写入方面吗?或者,为什么它不应该作为维护/报告问题进行阅读?我再次质疑商店的实用性。应该去哪里:)

3 个答案:

答案 0 :(得分:11)

这是一个有人指出我的长期存在的问题。为什么更好地将事件存储在写入方面有很多原因。

根据我的理解,你所谈论的架构是一个非常常见的架构,我看到......失败了。我们将域模型存储在关系数据库中,然后输出事件。您可以添加它们的扭曲,在事件存储中保存读取侧的事件。这可能会导致混乱。

您将遇到的第一个问题是发布您的活动。当我保存到数据库并发布说MSMQ(我死在中间)时会发生什么。因此DTC在它们之间引入。这是一个巨大的东西,应该像瘟疫一样避免分布式交易。它也是非常低效的,因为我可能使数据持久两次(一次排队一次到数据库)。这将大大限制系统吞吐量(DTC基准测试通常为200-300条消息/秒,事件通常只有20-30k /秒)。

有些人通过在其数据库中放置具有事件并作为队列运行的表来解决对DTC的需求。这将避免需要DTC,但这仍然会遇到下一个问题。

遇到错误会怎样?我知道你永远不会编写有缺陷的代码,但后来有一个Jrs /维护开发人员在使用该项目。举例来说,当域对象发生更改并且引发的事件不匹配时会发生什么?假设您将域对象上的State设置为“LA”(硬编码),但是您正确地将事件上的State设置为cmd.State(“CT”)。

您如何检测此类错误?正在讨论的最大问题是,现在有两个“真相”来源,即写入端的数据库和事件流出来。没有办法证明它们是等价的。这将导致各种奇怪的错误。

答案 1 :(得分:4)

我认为这是一个非常好的问题。将聚合视为一系列事件在写入方面本身很有用,使命令重试等更容易。但我同意,如果你需要这个快照性能改进,那么创建你的事件似乎很烦人,然后必须为你的对象制作另一个持久性模型。

一个系统,您的聚合只存储快照,但是将事件发送到读取模型以投影到读取模型中我认为会被称为“CQRS”,而不是“事件采购”。如果你把事件保留下来进行重新投影,我想你的系统就是两者兼而有之。

但是你不会有三个定义吗?一个用于保存聚合,一个用于通信状态更改,还有多个用于回答查询?

在这样的系统中,通过加载聚合并直接向他们提问来开始回答查询是很诱人的。虽然这不是以任何方式禁止的,但它确实倾向于开始导致这些聚合增加他们可能不需要的功能,更不用说复杂的线程和事务了。

答案 2 :(得分:2)

在写入端具有事件存储的一个原因可能是在事件变为“事实”之前解决并发问题并且被分发/分派,例如,通过乐观锁定提交事件流。这样,在写入方面,您可以确保解析同一事件流(聚合)的并发“提交”,其中一个通过,另一个必须通过比较事件或传播以智能方式解决冲突与客户的冲突,从而拒绝了命令。

相关问题