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

Copy entire entity using EntityId in CQRS with eventSourcing

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

因此,为了实现这一点,我使用需要复制的 CopyForm 命令发送表单的 entityId。整个表单是从事件存储加载的,在引发事件时,我将事件作为 FormAdded 而不是 FormCopied 引发,这将添加与我们从 eventStore 加载的源表单完全相同的新表单,并设置新的 entityId 。但是这里的问题是为我正在复制的相同表单而不是复制的表单引发事件。我的框架不允许更改 entityId。默认情况下,框架设置我为源表单引发的命令的 entityId,并且为具有相同 entityId 的源表单引发事件。

有没有更好的方法来使用事件溯源在 CQRS 中为实体执行复制功能?

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

发布事件说明新事实:

FormCopied { OriginalEntityId, NewEntityId }

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

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

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

我们可以使用以下内容:

FormCopied { EntityId, OriginalEntityId }

这与 Bryan 的回答仅略有不同,因为它允许使用 EntityId 索引的事件存储。

FormCopied 事件将成为重新水化新 "Form" 实体的流的一部分似乎是正确的。但仍然存在问题。例如,假设接下来我们的用户尝试向 his/her 表单添加一个新字段。这是下一个可能的命令:

AddFieldToForm { EntityId, NewField }

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

FormCopied { EntityId, OriginalEntityId }

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

  1. 通过实际提取 OriginalEntityId 的事件流,然后重播它,就好像它的事件源自这个新实体一样 - 或者 -
  2. 通过加载与 OriginalEntityId 对应的原始实体并将其属性一一复制到新实体。

您选择哪种取决于您的框架的功能。