ArkerLabs/event-sourcing-nestjs

Error: Events must implement StorableEvent interface

Closed this issue · 5 comments

Event is not published.

Implementation:

Event

import { StorableEvent } from 'event-sourcing-nestjs';

export class AccountCreatedEvent extends StorableEvent {
  eventAggregate: 'account';
  eventVersion: 1;

  constructor(public readonly id: string, public readonly name: string) {
    super();
  }
}

Event Emitter

import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { Logger } from '@nestjs/common';
import { StoreEventBus } from 'event-sourcing-nestjs';

import { CreateAccountCommand } from '../impl/create-account.command';
import { AccountCreatedEvent } from 'src/account/events/impl/account-created.event';

@CommandHandler(CreateAccountCommand)
export class CreateAccountHandler
  implements ICommandHandler<CreateAccountCommand> {
  constructor(private readonly eventBus: StoreEventBus) {}

  async execute(command: CreateAccountCommand): Promise<string> {
    Logger.log('Async CreateUserHandler');

    const { name } = command;
    const id = '12345';

    this.eventBus.publish(new AccountCreatedEvent(id, name));

    return id;
  }
}

Event Handler

import { IEventHandler, EventsHandler } from '@nestjs/cqrs';
import { Logger } from '@nestjs/common';

import { AccountCreatedEvent } from '../impl/account-created.event';

@EventsHandler(AccountCreatedEvent)
export class UserCreatedHandler implements IEventHandler<AccountCreatedEvent> {
  handle(event: AccountCreatedEvent): void {
    Logger.log(event, 'AccountCreatedEvent');
  }
}

Expected behavior:

Display emitted event log

### Current behavior:

Error: Events must implement StorableEvent interface
    at StoreEventBus.publish (/Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/node_modules/event-sourcing-nestjs/dist/store-event-bus.js:25:19)
    at CreateAccountHandler.execute (/Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/dist/account/commands/handlers/create-account.handler.js:26:23)
    at CommandBus.execute (/Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/node_modules/@nestjs/cqrs/dist/command-bus.js:42:28)
    at AccountsService.createAccount (/Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/dist/account/services/account.service.js:23:31)
    at AccountController.createAccount (/Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/dist/account/controllers/account.controller.js:24:37)
    at /Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/node_modules/@nestjs/core/router/router-execution-context.js:37:29
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async /Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/node_modules/@nestjs/core/router/router-execution-context.js:45:28
    at async /Users/adrian/Projects/Yaydoo/connect/yaydoo-connect-api/node_modules/@nestjs/core/router/router-proxy.js:8:17

Environment:

Node version: v13.12.0
Yarn version: v1.22.4
Typescript version: 3.7.4

package.json

{
  "version": "0.0.1",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@nestjs/common": "^7.0.0",
    "@nestjs/core": "^7.0.0",
    "@nestjs/cqrs": "^7.0.0",
    "@nestjs/platform-express": "^7.0.0",
    "@nestjs/swagger": "^4.5.10",
    "class-validator": "^0.12.2",
    "dotenv": "^8.2.0",
    "event-sourcing-nestjs": "^1.1.3",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^6.5.4"
  },
  "devDependencies": {
    "@nestjs/cli": "^7.0.0",
    "@nestjs/schematics": "^7.0.0",
    "@nestjs/testing": "^7.0.0",
    "@types/express": "^4.17.3",
    "@types/jest": "25.2.3",
    "@types/node": "^13.9.1",
    "@types/supertest": "^2.0.8",
    "@typescript-eslint/eslint-plugin": "3.0.2",
    "@typescript-eslint/parser": "3.0.2",
    "eslint": "7.1.0",
    "eslint-config-prettier": "^6.10.0",
    "eslint-plugin-import": "^2.20.1",
    "jest": "26.0.1",
    "prettier": "^1.19.1",
    "supertest": "^4.0.2",
    "ts-jest": "26.1.0",
    "ts-loader": "^6.2.1",
    "ts-node": "^8.6.2",
    "tsconfig-paths": "^3.9.0",
    "typescript": "^3.7.4"
  },
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".spec.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
}
Nytyr commented

This is the code that checks and throws that exception:

 publish<T extends IEvent>(event: T): void {
    const storableEvent = (event as any) as StorableEvent;
    if (
      storableEvent.id === undefined ||
      storableEvent.eventAggregate === undefined ||
      storableEvent.eventVersion === undefined
    ) {
      throw new Error('Events must implement StorableEvent interface');
    }
    ...

Can you log the event before publishing it? Are any of these fields undefined?

Log added:

publish(event) {
        const storableEvent = event;
        console.log({ storableEvent }) // <------
        if (storableEvent.id === undefined ||
            storableEvent.eventAggregate === undefined ||
            storableEvent.eventVersion === undefined) {
            throw new Error('Events must implement StorableEvent interface');
        }
        this.eventStore
            .storeEvent(storableEvent)
            .then(() => this.eventBus.publish(event))
            .catch(err => {
                throw err;
            });
    }

This is the result:

{
  storableEvent: AccountCreatedEvent {
    eventName: 'AccountCreatedEvent',
    id: '12345',
    name: 'Por Cobrar'
  }
}

Isn't getting values from class attributes

export class AccountCreatedEvent extends StorableEvent {
  eventAggregate: 'account';
  eventVersion: 1;

  constructor(public readonly id: string, public readonly name: string) {
    super();
  }
}
Nytyr commented

Looks like eventAggregate is undefined, thats why the Store Event Bus is throwing the exception.

eventAggregate y eventVersion are undefined, but I initialized them as attributes in the class, is there something I'm doing wrong?

export class AccountCreatedEvent extends StorableEvent {
  eventAggregate: 'account'; <---- 
  eventVersion: 1;                    <----

  constructor(public readonly id: string, public readonly name: string) {
    super();
  }
}

Sorry, my bad, it was a syntax error, I used : instead of = to assign the value.

Do you have a channel where I can ask you questions regarding the implementation, I am just getting experience in Event Sourcing and CQRS, this is the first project I have to lead with these approaches.