【问题标题】:How do I map relations in an eventstore used in an eventsourced architecture?如何在事件源架构中使用的事件存储中映射关系?
【发布时间】:2019-08-09 13:30:18
【问题描述】:

我正在尝试在事件存储中构建关系。我是事件外包的新手,所以请多多包涵。 :-)

应该如何在事件存储中映射关系?你能给我一些建议吗?

想象一下,我有一个关于项目管理的领域。我有一个聚合,它是ProjectProject 聚合根 包含TasksDocumentsFilesFolders,它们是Project 中核心实体的集合。 我还有一个ProjectBranch,它可以是Project 聚合的一部分,但也可以单独查看。在ProjectBranch 中可以更改前面提到的集合,并且可以将ProjectBranch 再次合并到Project 中,从而更新Project 的集合。

某些流程类似于 VCS 系统。

应该如何映射这些关系以及我应该创建聚合和聚合根的哪种分离?

如果 Project 是唯一的聚合,则事件(我想)如下所示:

  • ProjectWasCreated [聚合]
  • ProjectDocumentWasCreated
  • ProjectTaskWasCreated
  • ProjectBranchWasCreated
  • ProjectBranchDocumentWasCreated
    (这个事件如何知道文档属于哪个分支)

一旦 ProjectBranchWasMergedToProject 事件发生,ProjectBranch 中发生的所有事件都必须以某种方式在项目上重播。

另一方面,可能会有一个更相关的结构,其中有几个单独的聚合 - 例如ProjectProjectBranchTaskDocument 等等。

这意味着域有一组不同的事件,如下所示:

  • ProjectWasCreated [聚合]
  • DocumentWasCreated [聚合]
  • ProjectDocumentWasAttached(documentId)
  • ProjectBranchWasCreated(projectId) [聚合]
  • DocumentWasCreated [聚合]
  • ProjectBranchDocumentWasAttached(documentId)

其中一些功能可能需要在Project 之外独立工作,因此它们将被制成独立模块。

谢谢:-)

【问题讨论】:

    标签: events domain-driven-design aggregate relationship event-sourcing


    【解决方案1】:

    假设所有这些元素都是聚合:ProjectProjectBranchTaskDocument,等等。

    构建聚合的基本原则之一是它们形成事务一致性边界,这意味着在单个聚合内,所有元素必须是一致的,并且在事务发生时满足相关的业务规则。

    这就是为什么人们通常坚持使用小型聚合结构的原因,大多数聚合中只有一个实体。随着Project 的增长,您将不可能使所有这些元素保持同步和一致。

    现在谈到你的问题,关系的答案分为两部分:

    1. 聚合之间的所有链接都应采用聚合身份的形式。如果Task 链接到Project,则Task 聚合事件将包含ProjectId 作为属性。

      您不应该将聚合结构存储在另一个内部。

      如果您使用的是 RDBMS,则聚合之间所需的任何同步(例如,如果项目已关闭)都应在域事件的帮助下完成。

      但由于您使用的是 EventSourcing,因此无需在后台执行此操作。您动态构建聚合结构,这将我们带到第二点。

    2. 与任何其他 EventSource 投影一样,当您构造聚合对象时,您将需要重构内部数据元素。

      如果您希望项目结构作为任务投影的一部分可用,请调用项目应用程序服务以实时检索项目聚合。

      对于您可能希望作为投影一部分的所有链接聚合,依此类推。

    【讨论】:

    • 谢谢,Subhash。我试图完全理解你的答案。我的印象是,事件有效负载不能包含关系引用。这可能(非常)错误。下面发布了几个后续问题:
    • 如果有效负载包含对另一个聚合的引用,我将如何确保基本聚合没有从“外部”更改?是通过投影吗?
    • 2) 如果我想象一个聚合,其中域中的聚合表示具有父类型和子类型,那么我是否也存储聚合类型?这意味着每个事件我将拥有以下字段:aggregate_id、aggregate_type、event、event_version、sequence_number、payload、actor_id、timestamp。
    • 1.是,对的。每个聚合将与一个单独的事件流相关联。如果您在项目的事件流中引用了任务,则从其事件流构建任务对象而忽略对自身的引用。过去,要大规模完成此任务,我发现最好的方法是创建滚动快照。快照是聚合在给定点的当前状态的投影,并表示到该点的所有事件都已重播时的状态。您可以使用滚动快照作为启发式方法,以防止需要加载聚合的整个历史记录的所有事件。
    • 2.如果您有单独的流,则无需存储 aggregate_type,每个聚合一个。能否举例说明一下,让我更好理解?
    猜你喜欢
    • 2014-11-21
    • 2017-06-27
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 1970-01-01
    • 2021-02-01
    相关资源
    最近更新 更多