【问题标题】:How to manage ViewModel changes in a CQRS + Event Sourcing Architecture如何在 CQRS + 事件溯源架构中管理 ViewModel 更改
【发布时间】:2011-07-31 17:24:22
【问题描述】:

我们目前正在评估 CQRS 和事件溯源架构。我试图了解使用这种设计的维护含义是什么。我正在努力寻找答案的两个问题是:

1) 如果在应用程序启动并运行一段时间后,需要向 ReadModel 数据库上的 ViewModel 添加附加字段,会发生什么情况?比如说,在 CustomerList ViewModel 上需要客户邮政编码,而以前不需要。因此,可以轻松地将额外的列添加到 ViewModel 数据库中,但是如何填充呢?据我所知,唯一的方法是清除读取数据库,并从头开始重播所有事件以备份 ReadModel 数据库。但是,如果应用程序已经启动并运行了数月或数年(如我们所愿),该怎么办。这可能是要重播的数百万个事件,只是为邮政编码列添加数据。

如果出于某种技术原因,ReadModel 数据库不同步,或者我们想要添加一个新的 ReadModel 数据库,我也有同样的担忧。似乎应用程序越旧,使用得越多,获得最新的 readmodel 就越困难和昂贵。还是我在某个地方错过了一个技巧? ReadModel 快照之类的东西?

2) 如果在重播所有数百万个事件以备份读取数据库之后,一些数据与预期不符(即看起来错误),会发生什么情况。人们认为,可能是事件存储中的某个错误或非规范化例程可能导致了这种情况(似乎如果在编码中可以依赖一件事,那就是错误)。如何去调试这个!这似乎是一项不可能完成的任务。或者,也许,我又错过了一个技巧。

我很想听听已经运行这样一个系统一段时间的任何人的意见,维护和升级路径对您来说是如何解决的。

感谢您的宝贵时间和意见。

【问题讨论】:

    标签: design-patterns architecture cqrs event-sourcing


    【解决方案1】:

    如前所述,将事件溯源与 CQRS 结合使用的美妙之处在于能够破坏读取模型并从头开始重建它。出于某种原因,人们有这样的想法,即在您超过任意数量的事件之后需要很长时间。如果您为读取模型使用关系数据库——而且很可能是——打开事务很容易,通过处理程序读取所有事件,然后提交事务。只有当事务提交时,我们才真正接触到磁盘。其他所有内容都在内存中执行,因此可以闪电般快速。事实上,如果您的系统在短短几分钟内完成数百万个事件,我不会感到惊讶。

    从头开始重建您的读取模型应该与您将事件非规范化为读取模型的日常方法完全相同。如果没有,您的读取模型非规范化代码中存在错误。这里的好处是,从您的消息处理程序的角度来看,在常规/生产场景和读取模型重建场景中接收和非规范化到读取模型中的事件没有区别。

    如果您确实遇到错误,您可以通过将生产事件流式传输/复制到本地工作站,在处理程序中设置断点,然后通过读取的模型处理代码运行这些事件来轻松调试。

    【讨论】:

    • 感谢您的回复。您对事件重播时间不是问题的看法(即使对于数百万个事件)令人放心。顺便说一句,我喜欢你的博客,谢谢分享!
    • 你肯定想做一些测试,因为如果你做错了视图模型填充可能会很慢。需要一点点努力才能让它变快。
    • 我们一直在实践或在每次推动生产时重建我们的读取模型。我们首先重建数据,将其推送到我们的阶段读取模型,如果重建成功,我们将进行重建并将其推送到我们的生产读取模型。对我们来说,它确保读取模型反映每个版本中对事件处理程序的所有修改。
    • “如前所述,将事件溯源与 CQRS 结合使用的美妙之处在于能够破坏读取模型并从头开始重建它”。采用CQRS风格的奇怪原因,破坏数据库的能力......
    • 即使您正在删除可能包含事件投影的数据库,您也不会丢失任何数据。相反,您通常只会在业务需求发生变化并且不再需要相关特定视图时删除数据库。然后,您可以针对一组新的处理程序重新播放您的事件,以根据当前的业务需求创建不同的视图/投影——所有这些都不会真正丢失数据。
    【解决方案2】:

    我对 CQRS 有点陌生,所以这可能不是最可取的途径(但 iirc 我是从 CQRS/DDDD 邮件列表之一中选择的)。

    我们针对预期运行一次然后弃用的目的创建一个命令和相应的处理程序。

    在处理程序中,我们使用任何方便的机制,因此在您添加邮政编码字段的情况下,我们可能会运行一次性查询,从另一个视图模型中提取当时的邮政编码并填充新列。我们不太担心这些场景中的架构纯度,因为它预计是一次性操作(Rob Conery's Massive 已在这些情况下成功使用)。

    【讨论】:

    • 是的,一位将几个 ES/CQRS 架构投入生产的工程师告诉我,他们曾经将数据模型更改视为常规事件并尝试以最佳方式管理它们。当读取端必须处理属性取消而不是添加时,这尤其棘手,因为在这种情况下,您只需更新代码(当强类型时)并提供默认值
    【解决方案3】:

    我还没有使用带有事件源的 cqrs 的生产就绪应用程序,所以这里只是我尝试构建一个的经验。

    1)Read Model rebuild。是的,一旦其中的某些内容发生变化,您基本上必须重建整个读取模型数据库。如果有很多事件,这可能需要很长时间。所以读取模型重建必须高度优化(使用事件批处理等)。我觉得事件溯源最适合读写比率高的情况。因此,对于一些极易波动的数据,最好不要将其存储为域事件。但是关于存储容量的问题也不是那么遥远。在任何情况下,您都可以将 cqrs 应用于系统的一部分,它最适合的部分(例如,我可能不会将图形图像存储为事件的一部分)。

    2)Debugging。 事件存储中出现错误的可能性很小(这应该是框架的关注点),并且总是很容易检查存储中的事件。至于产生预期事件的命令,这里应该有测试,这些测试可能是系统中最有价值的测试。对于非规范化器,您也可以进行测试,但如果可以用肉眼看到它们的正确性,我就不会为琐碎的非规范化器编写测试。话虽如此,我使用调试器几次来发现一些更复杂的非规范化器中的问题;试图确定是哪个事件使事情出错并没有那么有趣。

    【讨论】:

      【解决方案4】:

      也可以在您的模型中添加净额结算事件。这可以在收到 X 个事件(比如 500 个)后作为任意任务运行

      要重建,您将事件推送到堆栈上,直到遇到净额事件,这将用作基线,从这里您将事件从堆栈中弹出,将它们的值与您的基线事件聚合起来。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-31
        • 1970-01-01
        • 2017-08-14
        • 1970-01-01
        • 1970-01-01
        • 2018-11-15
        • 2019-09-22
        • 1970-01-01
        相关资源
        最近更新 更多