使用eventSourcing在CQRS中使用EntityId复制整个实体

本文关键字:复制 EntityId 实体 eventSourcing CQRS 使用 | 更新日期: 2023-09-27 18:10:19

我正在使用带有事件源的CQRS。我有一个实体。带有entityId的表单。现在我必须在这个实体上发送复制命令(CommandName: CopyForm, EventName: formcopy),所以,整个表单应该被复制并具有不同的entityId。

所以,要实现这一点,我发送的形式与CopyForm命令的entityId需要复制。整个表单是从事件存储中加载的,而引发事件时,我将事件作为FormAdded而不是formcopy,这将添加新表单,与我们从eventStore加载的源表单完全相同,只是设置新的entityId。但这里的问题是,我复制的是相同形式的事件,而不是复制的形式。我的框架不允许更改entityId。框架默认设置命令的entityId,我正在为源表单和事件的源表单提出相同的entityId。

是否有更好的方法在事件源的CQRS实体复制功能?

使用eventSourcing在CQRS中使用EntityId复制整个实体

发布声明新事实的事件:

FormCopied { OriginalEntityId, NewEntityId }

结合域的历史记录,您现在有足够的信息来确切地知道副本应该包含什么,以及两个实体(带有id)的关系。

Bryan的回答很好,但是有点不完整。

在事件溯源系统中,域的命令服务需要能够从事件日志中获取实体。无论事件日志的后备存储是什么,它都可能由EntityID索引,实体将通过从后备数据存储中提取给定EntityID的所有事件来重新补充。因此,FormCopied事件需要有一个标准的EntityId字段,不是吗?

我们可以使用以下语句:

FormCopied { EntityId, OriginalEntityId }

这与Bryan的回答只有轻微的不同,因为它允许一个由EntityId索引的事件存储。

这似乎是正确的,FormCopied事件将是流的一部分,将补充新的"Form"实体。但问题依然存在。例如,假设接下来我们的用户试图在他/她的表单中添加一个新字段。下面可能是下一个命令:
AddFieldToForm { EntityId, NewField }

现在,想象一下这个"Form"实体有一些业务规则要检查,就像实体经常做的那样。命令服务将希望加载新的"Form"实体,以便检查业务规则,因此它将尝试从事件日志中重新填充"Form"实体。我们提取事件日志,只找到一个事件。

FormCopied { EntityId, OriginalEntityId }

所以现在我们必须回到事件日志,并为OriginalEntityId提取事件流,以便获得新实体的完全复水副本。至少有两种方法:

  1. 通过实际拉出OriginalEntityId的事件流,然后重放它,就好像它的事件起源于这个新实体-或-
  2. 通过加载与OriginalEntityId对应的原始实体,并将其属性一个接一个地复制到新实体。

选择哪一个取决于框架的能力。

您的情况似乎有点奇怪,但无论如何,我能想到的最简单的方法是获取表单的事件流,复制它们并替换副本中的实体Id(您可以这样做,因为事件是简单的数据结构)。然后保存并发布新的事件流。如果复制的事实具有域意义,则FormAdded事件可以有一个属性IsCopied