{"_id":"@quiqupltd/event-sourcing-nestjs","_rev":"26-57139d539c8155a02c8986ef571f788b","name":"@quiqupltd/event-sourcing-nestjs","dist-tags":{"next":"1.1.4","latest":"1.1.4"},"versions":{"1.1.4":{"name":"@quiqupltd/event-sourcing-nestjs","version":"1.1.4","keywords":["nestjs","es","eventsourcing","cqrs","ddd"],"author":{"name":"ArkerLabs"},"license":"ISC","_id":"@quiqupltd/event-sourcing-nestjs@1.1.4","maintainers":[{"name":"ant0h","email":"anthony@quiqup.com"},{"name":"ninobc","email":"joe@quiqup.com"},{"name":"nader-quiqup","email":"nader@quiqup.com"},{"name":"anam131","email":"anam@quiqup.com"},{"name":"quiqup-invoice","email":"invoices@quiqup.com"},{"name":"dannyhawkins","email":"danny@quiqup.com"},{"name":"tutts","email":"samueltutton@gmail.com"},{"name":"julianb","email":"julianbetancourt10@gmail.com"},{"name":"rafaeltravell88","email":"rafaelsoares@fastmail.com"},{"name":"luizvarela","email":"luiz.lvarela@gmail.com"}],"homepage":"https://github.com/ArkerLabs/event-sourcing-nestjs#readme","bugs":{"url":"https://github.com/ArkerLabs/event-sourcing-nestjs/issues"},"dist":{"shasum":"0e3481dbdbac6c623f73f54e5aa59c3777af3e2c","tarball":"https://registry.npmjs.org/@quiqupltd/event-sourcing-nestjs/-/event-sourcing-nestjs-1.1.4.tgz","fileCount":53,"integrity":"sha512-v0WsJonwV0duDCnG7Su3UZ/YkfupNtAkae2u1Q3KAkUGTDbyEJu+dkoe69F94eW9XN9utGatiGhcbzoe9MWdqw==","signatures":[{"sig":"MEUCIDz7RTeF5UfMcfpHSE/ctXmjS57rbCTO2HIpQZazt/2PAiEAiv9iVA8cJg7AK9g9PBdkPHrvepMjie122FzCx9+6Hw8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":52051,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDyNFCRA9TVsSAnZWagAApQAP/AmCcbBOFbT5gBuax4ft\nWuz+eddkGCTtZKiHgGQN1A0kvUjNTL0PS4pa4qvXoruwT+AxBjNWf4P1k7J8\no/f8k0XnU7Xvy6qN+DN+8vabRpkKOuOAUhS2lCU/j41S6lf2fqaXi1f8ea32\noOOKvh7xN6uBOEvQbdFMn0p1Zb1rftB5Wvu2RsIQcCMl5r4PWfCTxungsaL+\nA63e/lcIqhvLscuMFX8Tsoumi6NHzzN/jo2oJRucauBI4rk/x0g+UZcqF/sv\nhWaRYhwKOnkQi8lgIO3TK7yKxg8LGQmMHCJDnjNaAWRBk0sqlAurdmhNpeEv\ngi3uKEHJpQ2/Q7Kn9lUlFpYOqvR1U4xJMpiH7+G2hqUvfMwhoqagGXkfG2MM\ny2bkF6pkjQDuAgDyYBw3/sbbwANs+4De1Sz4dVLmnK5uUtM53sDhM5yPOFnS\nap5hHCRwRWuGa5LZx2YA80NtndXBqmrr6vINDVCupyX5MB3xe00zPvbfGdgh\nippZs0bisZnPBbXaj34XWQ1ewcZHuXtdPpWA1Y1z2Rh6T/XVNB+3dhzvsZ7W\ntgAjmpZN3+VSdcyFGNa6Kve5cKqlJ6USQtQHN+PFjqDU4LNt5Xz37pxwlKAf\nJ3suMBvU0tEWA57QIyg7kyaUPWqZoVC+CQ86400RspAlwT4kWrAEwUUoR5fT\nRmVn\r\n=mfcC\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","husky":{"hooks":{"commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"gitHead":"e89e5ebbab006f455093a8dbf1617aa4c814d230","scripts":{"lint":"eslint \"{src,test}/**/*.ts\" --fix && npm run prettier","build":"tsc -p tsconfig.json","prebuild":"rm -rf dist","prettier":"prettier --write 'src/*.{ts, js}'","publish:npm":"npm publish --access public","publish:next":"npm publish --access public --tag next","prepublish:npm":"npm run build","prepublish:next":"npm run build"},"_npmUser":{"name":"dannyhawkins","email":"danny@quiqup.com"},"repository":{"url":"git+https://github.com/ArkerLabs/event-sourcing-nestjs.git","type":"git"},"_npmVersion":"6.14.5","description":"NestJS module for implementing Event Sourcing.","directories":{},"_nodeVersion":"14.5.0","dependencies":{"mongodb":"^3.5.2","eventstore":"^1.15.2","@types/node":"^13.5.0"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^6.5.4","husky":"^4.2.3","eslint":"^6.8.0","ts-node":"^8.6.2","prettier":"^2.0.1","typescript":"^3.8.3","@nestjs/core":"^6.7.2","@nestjs/cqrs":"^6.1.0","@nestjs/common":"6.10.14","tsconfig-paths":"^3.9.0","@commitlint/cli":"^8.3.5","reflect-metadata":"^0.1.13","eslint-plugin-import":"^2.20.1","eslint-config-prettier":"^6.10.0","@nestjs/platform-express":"^6.11.1","@typescript-eslint/parser":"^2.23.0","@commitlint/config-conventional":"^8.3.4","@typescript-eslint/eslint-plugin":"^2.23.0"},"_npmOperationalInternal":{"tmp":"tmp/event-sourcing-nestjs_1.1.4_1594827588489_0.9366888893555974","host":"s3://npm-registry-packages"}}},"time":{"created":"2020-07-15T15:39:48.394Z","modified":"2025-07-07T18:39:13.063Z","1.1.4":"2020-07-15T15:39:48.714Z"},"bugs":{"url":"https://github.com/ArkerLabs/event-sourcing-nestjs/issues"},"author":{"name":"ArkerLabs"},"license":"ISC","homepage":"https://github.com/ArkerLabs/event-sourcing-nestjs#readme","keywords":["nestjs","es","eventsourcing","cqrs","ddd"],"repository":{"url":"git+https://github.com/ArkerLabs/event-sourcing-nestjs.git","type":"git"},"description":"NestJS module for implementing Event Sourcing.","maintainers":[{"email":"invoices@quiqup.com","name":"quiqup-invoice"},{"email":"danny@quiqup.com","name":"dannyhawkins"},{"email":"luiz.lvarela@gmail.com","name":"luizvarela"},{"email":"nader@quiqup.com","name":"nader-quiqup"},{"email":"hassan@quiqup.com","name":"hassankourani"},{"email":"hussein.m@quiqup.com","name":"husseinmoussa"},{"email":"svetoslav@quiqup.com","name":"slavaquiqup"},{"email":"federico@quiqup.com","name":"ff3derico"},{"email":"sergiomurilovarela@gmail.com","name":"murilovarelaquiqup"}],"readme":"✨ Event Sourcing for Nestjs\n=====\n\n[![](https://badgen.net/npm/v/event-sourcing-nestjs)](https://www.npmjs.com/package/event-sourcing-nestjs) ![](https://badgen.net/npm/dt/event-sourcing-nestjs) \n\nLibrary that implements event sourcing using NestJS and his CQRS library.\n\n## ⭐️ Features\n* **StoreEventBus**: A class that replaces Nest's EventBus to also persists events in mongodb.\n* **StoreEventPublisher**: A class that replaces Nest's EventPublisher.\n* **ViewUpdaterHandler**: The EventBus will also delegate the Events to his View Updaters, so you can update your read database.\n* **Replay**: You can re-run stored events. This will only trigger the view updater handlers to reconstruct your read db.\n* **EventStore**: Get history of events for an aggregate.\n\n\n## 📖 Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Importing](#importing)\n  - [Events](#events)\n  - [Event emitter](#event-emitter)\n  - [Event Publisher](#event-publisher)\n  - [Get event history](#get-event-history)\n    - [Full example](#full-example)\n  - [View updaters](#view-updaters)\n    - [State of the art](#state-of-the-art)\n- [Reconstructing the view db](#reconstructing-the-view-db)\n- [Examples](#examples)\n\n## 🛠 Installation\n```bash\nnpm install event-sourcing-nestjs @nestjs/cqrs --save\n```\n\n## Usage\n\n### Importing\n\napp.module.ts\n```ts\nimport { Module } from '@nestjs/common';\nimport { EventSourcingModule } from 'event-sourcing-nestjs';\n\n@Module({\n  imports: [\n    EventSourcingModule.forRoot({\n      mongoURL: 'mongodb://localhost:27017/eventstore',\n    }),\n  ],\n})\nexport class ApplicationModule {}\n```\n\nImporting it in your modules\n```ts\nimport { Module } from '@nestjs/common';\nimport { EventSourcingModule } from 'event-sourcing-nestjs';\n\n@Module({\n  imports: [\n    EventSourcingModule.forFeature(),\n  ],\n})\nexport class UserModule {}\n```\n\n### Events\nYour events must extend the abstract class StorableEvent.\n\n```ts\nexport class UserCreatedEvent extends StorableEvent {\n    eventAggregate = 'user';\n    eventVersion = 1;\n    id = '_id_';\n}\n```\n\n### Event emitter\nInstead of using Nest's EventBus use StoreEventBus, so events will persist before their handlers are executed.\n\n```ts\nimport { CommandHandler, ICommandHandler } from '@nestjs/cqrs';\nimport { StoreEventBus } from 'event-sourcing-nestjs';\n\n@CommandHandler(CreateUserCommand)\nexport class CreateUserHandler implements ICommandHandler<CreateUserCommand> {\n\n    constructor(\n        private readonly eventBus: StoreEventBus,\n    ) {}\n\n    async execute(command: CreateUserCommand) {\n        this.eventBus.publish(new UserCreatedEvent(command.name));\n    }\n\n}\n```\n\n### Event Publisher\nUse **StoreEventPublisher** if you want to dispatch events from your AggregateRoot and store it before calling their handlers.\n\n```ts\nimport { CommandHandler, ICommandHandler } from '@nestjs/cqrs';\nimport { HeroRepository } from '../../repository/hero.repository';\nimport { KillDragonCommand } from '../impl/kill-dragon.command';\nimport { StoreEventPublisher } from 'event-sourcing-nestjs';\n\n@CommandHandler(KillDragonCommand)\nexport class KillDragonHandler implements ICommandHandler<KillDragonCommand> {\n  constructor(\n    private readonly repository: HeroRepository,\n    private readonly publisher: StoreEventPublisher,\n  ) {}\n\n  async execute(command: KillDragonCommand) {\n    const { heroId, dragonId } = command;\n    const hero = this.publisher.mergeObjectContext(\n      await this.repository.findOneById(heroId),\n    );\n    hero.killEnemy(dragonId);\n    hero.commit();\n  }\n}\n```\n\n### Get event history \n\nReconstruct an aggregate getting his event history.\n\n```ts\nconst aggregate = 'user';\nconst id = '_id_';\nconsole.log(await this.eventStore.getEvents(aggregate, id));\n```\n\n#### Full example\n\nhero-killed-dragon.event.ts\n```ts\nimport { StorableEvent } from 'event-sourcing-nestjs';\n\nexport class HeroKilledDragonEvent extends StorableEvent {\n\n  eventAggregate = 'hero';\n  eventVersion = 1;\n  \n  constructor(\n    public readonly id: string,\n    public readonly dragonId: string,\n  ) {\n    super();\n  }\n}\n```\n\nhero.model.ts\n```ts\nimport { AggregateRoot } from '@nestjs/cqrs';\n\nexport class Hero extends AggregateRoot {\n\n  public readonly id: string;\n\n  public dragonsKilled: string[] = [];\n\n  constructor(id: string) {\n    super();\n    this.id = id;\n  }\n\n  killEnemy(enemyId: string) {\n    this.apply(new HeroKilledDragonEvent(this.id, enemyId));\n  }\n\n  onHeroKilledDragonEvent(event: HeroKilledDragonEvent) {\n    this.dragonsKilled.push(event.dragonId);\n  }\n\n}\n```\n\nhero.repository.ts\n```ts\nimport { Injectable } from '@nestjs/common';\nimport { Hero } from '../models/hero.model';\nimport { EventStore } from 'event-sourcing-nestjs';\n\n@Injectable()\nexport class HeroRepository {\n\n  constructor(\n    private readonly eventStore: EventStore,\n  ) {}\n\n  async findOneById(id: string): Promise<Hero> {\n    const hero = new Hero(id);\n    hero.loadFromHistory(await this.eventStore.getEvents('hero', id));\n    return hero;\n  }\n}\n```\n\n\n### View updaters\n\n\n#### State of the art\n![State of the art](https://raw.githubusercontent.com/ArkerLabs/event-sourcing-nestjs/master/docs/state.jpg)\n\n\nAfter emitting an event, use a view updater to update the read database state.\nThis view updaters will be used to recontruct the db if needed.\n\nRead more info about the Materialized View pattern [here](https://docs.microsoft.com/en-gb/azure/architecture/patterns/materialized-view)\n\n```ts\nimport { IViewUpdater, ViewUpdater } from 'event-sourcing-nestjs';\n\n@ViewUpdater(UserCreatedEvent)\nexport class UserCreatedUpdater implements IViewUpdater<UserCreatedEvent> {\n\n    async handle(event: UserCreatedEvent) {\n        // Save user into our view db\n    }\n}\n```\n\n## Reconstructing the view db\n\n```ts\nawait ReconstructViewDb.run(await NestFactory.create(AppModule.forRoot()));\n```\n\n\n\n## Examples\nYou can find a working example using the Materialized View pattern [here](https://github.com/ArkerLabs/event-sourcing-nestjs-example).\n\nAlso a working example with Nest aggregates working [here](https://github.com/Nytyr/nest-cqrs-eventsourcing-example).\n\n## TODOs\n* Use snapshots, so we can reconstruct the aggregates faster.\n\n\n## 📝 Stay in touch\n\n- Author - [Nytyr](https://keybase.io/nytyr)\n- Website - [https://arkerlabs.com/](https://arkerlabs.com/)\n","readmeFilename":"README.md"}