Skip to content

hugoviktor/event-sourcing-nestjs

 
 

Repository files navigation

Event Sourcing for Nestjs

npm version

Library that implements event sourcing using NestJS and his CQRS library.

Features

  • StoreEventBus: A class that replaces Nest's EventBus to also persists events in mongodb.
  • StoreEventPublisher: A class that replaces Nest's EventPublisher.
  • ViewUpdaterHandler: The EventBus will also delegate the Events to his View Updaters, so you can update your read database.
  • Replay: You can re-run stored events. This will only trigger the view updater handlers to reconstruct your read db.
  • EventStore: Get history of events for an aggregate.

State of the art

State of the art

Getting started

npm install event-sourcing-nestjs @nestjs/cqrs --save

Usage

Importing

app.module.ts

import { Module } from '@nestjs/common';
import { EventSourcingModule } from 'event-sourcing-nestjs';

@Module({
  imports: [
    EventSourcingModule.forRoot({
      mongoURL: 'mongodb://localhost:27017/eventstore',
    }),
  ],
})
export class ApplicationModule {}

Using it in your modules

import { Module } from '@nestjs/common';
import { EventSourcingModule } from 'event-sourcing-nestjs';

@Module({
  imports: [
    EventSourcingModule.forFeature(),
  ],
})
export class UserModule {}

Event emitter

Instead of using Nest's EventBus use StoreEventBus, so events will persist before their handlers are executed.

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

@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {

    constructor(
        private readonly eventBus: StoreEventBus,
    ) {}

    async execute(command: CreateUserCommand) {
        this.eventBus.publish(new UserCreatedEvent(command.name));
    }

}

Or use StoreEventPublisher if you want to dispatch event from your AggregateRoot.

View updaters

After emitting an event, use a view updater to update the read database state. This view updaters will be used to recontruct the db if needed.

Read more info about the Materialized View pattern here

import { IViewUpdater, ViewUpdater } from 'event-sourcing-nestjs';

@ViewUpdater(UserCreatedEvent)
export class UserCreatedUpdater implements IViewUpdater<UserCreatedEvent> {

    async handle(event: UserCreatedEvent) {
        // Save user into our view db
    }
}

Events

Finally, your events must extend the abstract class StorableEvent.

export class UserCreatedEvent extends StorableEvent {
    eventAggregate = 'user';
    eventVersion = 1;
    id = '_id_';
}

Get event history for an aggregate

const aggregate = 'user';
const id = '_id_';
console.log(await this.eventStore.getEvents(aggregate, id));

Reconstructing the view db

await ReconstructViewDb.run(await NestFactory.create(AppModule.forRoot()));

Examples

You can find a working example using the Materialized View pattern here.

Also a working example with Nest aggregates working here.

TODOs

  • Write a better documentation explaining patterns, methods, etc.
  • Use snapshots, so we can reconstruct the aggregates faster.

About

NestJS module for implementing Event Sourcing

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 98.4%
  • JavaScript 1.6%