【问题标题】:Preferred way of handling "Event Sourcing" in NestJS CQRS recipe在 NestJS CQRS 配方中处理“事件溯源”的首选方式
【发布时间】:2019-05-23 04:13:44
【问题描述】:

我一直在尝试找出在使用 NestJS CQRS 配方 (https://docs.nestjs.com/recipes/cqrs) 时进行“事件溯源”的首选方式。

在过去的几周里,我一直在研究 NestJS 框架,并且喜欢它的各个方面。除了文档,它们在某些方面非常薄。

要么 NestJS 对如何实现“事件溯源”没有真正的意见,要么我遗漏了一些明显的东西。

我的主要问题是:保持事件本身最简单的方法是什么?

现在,我的活动看起来很基本:

import { IEvent } from '@nestjs/cqrs';

export class BookingChangedTitleEvent implements IEvent {
    constructor(
        public readonly bookingId: string,
        public readonly title: string) {}
}

我最初的想法是使用 TypeORM (https://docs.nestjs.com/recipes/sql-typeorm) 并让我的每个事件不仅实现 IEvent,还要让它继承一个 TypeORM @Entity()

但是对于每个事件,这将有一个表 (SQL) 或集合 (NoSQL),因此无法读取发生在单个聚合中的所有事件。我错过了什么吗?

另一种方法是将每个事件转储到 JSON,这听起来很容易。但是我将如何从数据库中加载对象IEvent 类呢? (听起来我正在实现自己的 ORM)

【问题讨论】:

    标签: node.js typescript nestjs cqrs event-sourcing


    【解决方案1】:

    所以我正在做类似的事情并使用 postgres,它确实在 TypeORM 白话 (reference) 中支持 json ('simple-json')。无论好坏,我的事件实体看起来像:

    @Entity()
    export class MyEvent {
      @PrimaryGeneratedColumn('uuid')
      id: string;
    
      @Column()
      name: string;
    
      @Column('simple-json')
      data: object;
    
      @CreateDateColumn({type: 'timestamp'})
      created_at: Date;
    }

    请务必注意,我仅将持久事件用于审计跟踪以及我尚未构建的潜在预测的灵活性。您完全可以使用 TypeORM 在 postgres 中查询 JSON,例如。 .where('my_event.data ::jsonb @> :data', {data: {someDataField: 2}}),但我的理解是查询您的事件以获取当前状态有点缺少 CQRS 的意义。最好在新的投影表中建立聚合或更新一个巨大的投影。

    我对我目前坚持我的事件的方式很好,但它肯定不是 DRY。我认为使用通用saveEvent 方法扩展基类或使用将在其构造函数中获取存储库的 EventHandlerFactory 类会更简洁一些,而不是将存储库注入每个处理程序。

    也许有人有一些好的想法?

    【讨论】:

      【解决方案2】:

      我确信这会因持久层而有很大不同。使用 MongoDB 时,我使用 Mongoose 的松散事件模式,以及聚合事件所需的一些属性。

      事件本身只是简单的类,例如:

      class FooHappened {
        constructor(
          readonly root: string;
          readonly bar: string;
        ) {}
      }
      

      我一直在使用聚合根 ObjectIdroot 属性来构建读取模型,到目前为止效果很好。

      【讨论】:

        【解决方案3】:

        首先,您最初的预感是正确的:NestJS CQRS 模块对您如何实现事件溯源没有意见。原因是 CQRS 与 ES 不同。虽然您可以组合它们,但这完全是可选的。然后,如果您决定使用 ES,那么还有很多方法可以实现。

        您似乎希望将事件保存在关系数据库中,这可能是避免拥有第二个 NoSql 数据库的额外复杂性的好选择(您可以稍后切换到专用数据库,例如Eventstore,并且受益于专门的 ES 功能)。

        关于您的 SQL 模型,最佳做法是使用一个表来存储您的事件。 Kasey Speakman 的 Event Storage in Postgres 是一篇很好的文章来证明这一点。

        此处选择的Events 表格布局如下所示:

        CREATE TABLE IF NOT EXISTS Event
        (
            SequenceNum bigserial NOT NULL,
            StreamId uuid NOT NULL,
            Version int NOT NULL,
            Data jsonb NOT NULL,
            Type text NOT NULL,
            Meta jsonb NOT NULL,
            LogDate timestamptz NOT NULL DEFAULT now(),
            PRIMARY KEY (SequenceNum),
            UNIQUE (StreamId, Version),
            FOREIGN KEY (StreamId)
                REFERENCES Stream (StreamId)
        );
        

        文章清楚地描述了每一列的基本原理,但您可以使用基于StreamId + Version 的查询来构建聚合。 Meta 列可以保存元数据,例如userIdcorrelationId(这里是more info 的相关性)等。文章还提到了如何创建快照,在某些 情况下可能很方便(但在需要之前避免使用)。

        注意Type 列,它存储了事件类型,可用于反序列化(因此无需创建自己的 ORM ;)

        展示如何实现事件存储的其他项目有PostgreSQL Event Sourcing 和更完整的解决方案message-db

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-01-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-15
          • 2019-09-22
          相关资源
          最近更新 更多