如何从数据库中的“ EventStore”持久存储聚合/读取模型?

问题描述 投票:1回答:1

第一次尝试实现事件源和CQRS,但是在持久化聚合时陷入困境。

这是我现在的位置

  1. 我已经将“ EventStore”设置为流,“ foos”
  2. node-eventstore-client连接到它
  3. 我订阅有赶超的活动

这一切都很好。

借助eventAppeared事件处理程序功能,无论何时发生事件,我都可以构建聚合。很好,但是我该怎么办?

假设我构建并汇总了Foos列表

    [
      {
        id: 'some aggregate uuidv5 made from barId and bazId',
        barId: 'qwe',
        bazId: 'rty',
        isActive: true,
        history: [
          {
            id: 'some event uuid',
            isActive: true,
            timestamp: 123456788,
            eventType: 'IsActiveUpdated'
          }
          {
            id: 'some event uuid',
            barId: 'qwe',
            bazId: 'rty',
            timestamp: 123456789,
            eventType: 'FooCreated'
          }
        ]
      }
    ]

为了遵循CQRS,我将在读取模型中构建以上汇总,对吗?但是如何将这个聚合存储在数据库中?

我想只是一个nosql数据库就可以了,但是我肯定需要一个数据库,因为我会将gRPC APi放在该数据库和其他读取模型/聚集的前面。

但是从构建聚合到将其持久保存在数据库中,我实际上从什么开始呢?

我曾经尝试过非常简单的本教程https://blog.insiderattack.net/implementing-event-sourcing-and-cqrs-pattern-with-mongodb-66991e7b72be,因为您将mongodb都用作事件存储,并且只创建了一个聚合视图,并在传入新事件时更新该视图。它有缺陷和局限性(聚合管道),这就是为什么我现在将事件存储部分转向“ EventStore”。

但是如何持久化聚集,聚集当前是通过“ EventStore”中的事件构建并存储在代码/内存中的??

我觉得这可能是一个愚蠢的问题,但是我是否必须遍历数组中的每个项目并在db表/集合中插入每个项目,或者您是否有某种方式可以一次将整个数组/聚合转储到那里?

之后会发生什么?您是否为每个聚合创建一个物化视图并对此进行查询?

我愿意为此选择最佳的数据库,无论是postgres /其他rdbms,mongodb,cassandra,redis,表存储等。

最后一个问题。目前,我仅使用单个流“ foos”,但在此级别上,我希望新事件会频繁发生(每隔几秒钟左右),但据我了解,您仍然会坚持使用并使用实现来更新它意见对吗?

因此,考虑到barId和bazId可以组合使用来对事件进行分组,而不是单个流,我认为更专业的流(例如foos-barId-bazId)将是您可以尝试的方法,它可以降低频率传入的新事件使重新创建实体化视图变得有意义。

是否有一般的经验法则,如果更新频率低于特定限制,则不重新创建/更新/刷新实例化视图?那么唯一的替代方法是从普通表/集合中查询?

编辑:

最后,我试图制作一个只有2个rpcs的gRPC api-一个用于通过id获取单个foo,一个用于获取所有foos(具有用于按状态过滤的可选字段-但这并不重要) 。简化的原型看起来像这样:

rpc GetFoo(FooRequest) returns (Foo)
rpc GetFoos(FoosRequest) returns (FooResponse)

message FooRequest {
    string id = 1; // uuid
}

// If the optional status field is not specified, return all foos
message FoosRequest {
    // If this field is specified only return the Foos that has isActive true or false
    FooStatus status = 1;

    enum FooStatus {
        UNKNOWN = 0;
        ACTIVE = 1;
        INACTIVE = 2;
    }
}

message FoosResponse {
    repeated Foo foos;
}

message Foo {
    string id = 1; // uuid
    string bar_id = 2 // uuid
    string baz_id = 3 // uuid
    boolean is_active = 4;
    repeated Event history = 5;
    google.protobuf.Timestamp last_updated = 6;
}

message Event {
    string id = 1; // uuid
    google.protobuf.Any data = 2;
    google.protobuf.Timestamp timestamp = 3;
    string eventType = 4;
}

传入事件看起来像这样:

{
  id: 'some event uuid',
  barId: 'qwe',
  bazId: 'rty',
  timestamp: 123456789,
  eventType: 'FooCreated'
}

{
  id: 'some event uuid',
  isActive: true,
  timestamp: 123456788,
  eventType: 'IsActiveUpdated'
}

正如您所看到的,在gRPC API中没有uuid可以使GetFoo(uuid)成为可能,这就是为什么我将生成带有barId和bazId的uuidv5的原因,它将结合在一起成为有效的uuid。我在上面看到的投影/聚合中做到这一点。

此外,GetFoos rpc要么返回所有foo(如果状态字段未定义),要么返回具有与状态字段匹配的isActive的foo(如果指定)。

但是我还不知道如何从追赶订阅处理程序继续。

我将事件存储在“ EventStore”(https://eventstore.com/)中,使用具有追赶功能的订阅,我以所需的形式构建了带有Foo数组的聚集/投影,但能够获得来自我的gRPC API的ID的单个Foo,我想我需要将整个聚合/投影存储在某种数据库中,以便我可以连接并从gRPC API提取数据?每次出现新事件时,我都需要将该事件也添加到数据库中,或者它如何工作?

[我想我已经阅读了我可以在互联网上找到的所有资源,但仍然缺少一些关键信息来解决这个问题。

gRPC不是那么重要。我猜可能是REST,但我最大的问题是如何使汇总/投影的数据可用于API服务(可能还会需要更多API)?我想我需要将具有生成的uuid和历史字段的聚合/投影数据存储在数据库中,以便能够通过uuid从API服务中获取数据,但是从catchup事件来看,是什么数据库以及该存储过程是如何完成的我在哪里建立集合的处理程序?

cqrs event-sourcing get-event-store
1个回答
0
投票

我完全知道你的感觉!基本上,这就是我第一次尝试进行CQRS和ES时发生的事情。

我认为您在知识上有一些空白,我相信您会很快弥补。您可以在执行操作时从事件流中添加聚合。那就是您的总和持续存在。读取模型有所不同。让我解释一下...

您的读取模型是您用来对之运行查询并提供数据以显示给UI的东西。您的聚合不(直接)参与其中。实际上,它们应该被封装。这意味着您无法从外部“看到”它们的状态。即没有getter和setter,但具有ID的聚合ID除外。

本文为您提供了如何将它们组合在一起的有用概述:CQRS + Event Sourcing – Step by Step

想法是,当聚合更改状态时,只能通过其生成的事件来更改。您将该事件存储在事件存储中。该事件也会发布,以便可以更新读取的模型。

也查看您的聚合,它看起来更像是典型的读取模型对象或DTO。聚合对功能而不是属性感兴趣。因此,您会期望看到无效的公共功能,无法向集合发出命令。但不是像isActive或history这样的公共属性。

我希望这是有道理的。

© www.soinside.com 2019 - 2024. All rights reserved.