使用 EventSourcing 和大量事件进行聚合设计

Aggregate design with EventSourcing and large number of events

我想从 EventSourcing 开始冒险。作为游乐场,我有一个系统可以从以阵列形式组织的一组传感器中收集数据。每个传感器都有一个单一的值,比如温度。这个系统我需要的是

  1. 获取传感器读数的当前值
  2. 上个月的传感器值历史记录
  3. 当传感器值改变时,我必须计算数组 "status" 和 存储它(也是一个月)
  4. 数组"status"可由用户手动更正

阵列和传感器的数量正在增长。对于每个数组,我每秒都有很多读数。

现在我想将数组作为实体与传感器进行聚合。在这种情况下,每个传感器读数更新都会升级阵列聚合版本。这给了 > 10M 的一个月的变化。在这个设计中,我不能切断旧事件。我想不出一年数据后恢复 ReadModels 所需的时间。

我想我可以将当​​前状态存储为 CRUD table 并从阵列中删除传感器当前数据。只保留定义。然后我可以使用将处理传感器数据流的服务,检查数组 "status" 并将数组 "status" 保留为单独的聚合。服务会发出 "Sensor data update" 事件。此事件将触发 ReadModel 保持历史数据处理 1 个月的约束。我不会用传感器读数事件污染事件存储。在 Array "status" 的情况下,我将能够从事件存储中删除整个过去的 "status" 聚合。数组将仅保留传感器定义,因此 EventStore 相对较小。

我失去了完整的历史。我无法恢复 1 个月的信号历史记录 ReadModel。我必须格外注意不要破坏它。

目标是学习如何扩展 EventSourcing / CQRS 系统。如何在几小时而不是几天内处理大型 EventStore 并重建损坏的或膨胀新的 ReadModels。

这个想法适合 ES / CQRS 吗? (编辑:可以使用不是来自聚合的事件流更新 RM 吗?)

如何处理不断增长的事件存储和修复损坏的 ReadModel 的问题?

谢谢!

聚集了很多事件

聚合是写入端的术语(CQRS 中的 C)。聚合接收命令,并使用其状态将事件发送到事件存储中。聚合状态是使用事件存储中的事件构建的。因此,如果给定聚合有很多事件,则构建状态需要时间。

为了加速构建聚合状态,CQRS/ES 框架正在使用快照 - 这是为特定聚合版本存储的序列化聚合状态,因此您构建状态不是从时间的开始,但是从最新的快照开始。例如,您可以存储每 100 个事件的快照。如果您的投影功能发生变化,请不要忘记重建它们。 reSolve 等框架正在透明地为您执行此操作。

您的场景

在你的特定情况下,在我看来你的业务逻辑是微不足道的,这意味着你不需要聚合状态来计算任何东西或做出决定 - 没有业务逻辑,你基本上只是将事件存储为它们是由传感器产生的。因此,在您的自定义框架中,您可以避免在写入端构建聚合状态 - 只需将事件存储为传入的传感器数据。

在读取端,您将像往常一样使用事件流 - 收到事件后,您可以将其存储到具有必要分类或时隙的读取模型数据库中。

如果您不需要 ReadModel 中的旧数据 - 您可以在重建期间跳过旧事件 - 它应该非常快。

如果您不想在事件存储中存储旧事件 - 您可以删除它们,但这不再是真正的事件源。

Does this idea fits into ES / CQRS?

您需要非常小心的一件事是了解哪些信息在您的领域模型的控制下,哪些属于外部。

如果您的传感器是现实世界中的物理设备,广播读数,那么您的领域模型不是权威。该传感器数据可能会被读取、验证(即:传输中的消息没有损坏)和存储。换句话说,传感器测量是 events(过去),而不是 commands(命令)。将它们放入方便的数据存储中。

考虑到这一点,您需要仔细查看您的数组是域实体(读取传感器数据并做出有趣的决定)还是投影(传感器测量流的重组)。

查看 Udi Dahan 的 When to avoid CQRS 可能会有用。他在那里谈论的一件事是,如果做得好,聚合看起来像 processes.

简而言之,确保您使用正确的工具来解决您的问题。

就是说,是的——如果您有足够多的事件将它们折叠成一个投影并不容易,那就很难了。你必须看看你有多少预算来解决这个问题,并开始挖掘更 I/O 有效的事件表示,更有效的事件内存表示,批处理等。试图找到不同的分区方式不同核心之间的工作。

LMAX 很好地记录了他们在处理大量消息流方面的经验教训;搜索有关其架构的信息。