### Install Core Module with pnpm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the core event-sourcing module using pnpm. ```bash pnpm add @ocoda/event-sourcing ``` -------------------------------- ### Install MariaDB Integration with npm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the MariaDB integration library using npm. ```bash npm install @ocoda/event-sourcing-mariadb ``` -------------------------------- ### Install MariaDB Integration with yarn Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the MariaDB integration library using yarn. ```bash yarn add @ocoda/event-sourcing-mariadb ``` -------------------------------- ### Install Core Module with yarn Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the core event-sourcing module using yarn. ```bash yarn add @ocoda/event-sourcing ``` -------------------------------- ### Install DynamoDB Integration with npm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the DynamoDB integration library using npm. ```bash npm install @ocoda/event-sourcing-dynamodb ``` -------------------------------- ### Install PostgreSQL Integration with npm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the PostgreSQL integration library using npm. ```bash npm install @ocoda/event-sourcing-postgres ``` -------------------------------- ### Install Core Module with npm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the core event-sourcing module using npm. ```bash npm install @ocoda/event-sourcing ``` -------------------------------- ### Install DynamoDB Integration with yarn Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the DynamoDB integration library using yarn. ```bash yarn add @ocoda/event-sourcing-dynamodb ``` -------------------------------- ### Install PostgreSQL Integration with yarn Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the PostgreSQL integration library using yarn. ```bash yarn add @ocoda/event-sourcing-postgres ``` -------------------------------- ### Advanced EventSourcingModule Setup with Persistence Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Configure EventSourcingModule for persistence using asynchronous factory. This example uses PostgreSQL for event storage and MongoDB for snapshot storage. ```typescript import { Events } from './app.providers'; import { PostgresEventStore, type PostgresEventStoreConfig } from '@ocoda/event-sourcing-postgres'; import { MongoDBSnapshotStore, type MongoDBSnapshotStoreConfig } from '@ocoda/event-sourcing-mongodb'; @Module({ imports: [ EventSourcingModule.forRootAsync({ useFactory: () => ({ events: Events, eventStore: { driver: PostgresEventStore, host: '', port: 5432, user: '', password: '', database: '', }, snapshotStore: { driver: MongoDBSnapshotStore, url: '', }, }), }), ], }) export class AppModule {} ``` -------------------------------- ### Install DynamoDB Integration with pnpm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the DynamoDB integration library using pnpm. ```bash pnpm add @ocoda/event-sourcing-dynamodb ``` -------------------------------- ### Install MariaDB Integration with pnpm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the MariaDB integration library using pnpm. ```bash pnpm add @ocoda/event-sourcing-mariadb ``` -------------------------------- ### Install MongoDB Integration with npm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the MongoDB integration library using npm. ```bash npm install @ocoda/event-sourcing-mongodb ``` -------------------------------- ### Install MongoDB Integration with yarn Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the MongoDB integration library using yarn. ```bash yarn add @ocoda/event-sourcing-mongodb ``` -------------------------------- ### Start Docker Container for Integration Packages Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Starts a specific Docker container, like MariaDB, required for testing changes in integration packages. Ensure the container name matches your needs. ```bash docker compose up -d mariadb ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Installs all project dependencies using pnpm from the root directory. This is necessary for all packages in the workspace. ```bash pnpm install ``` -------------------------------- ### Basic EventSourcingModule Setup Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Configure the EventSourcingModule with an array of event classes for basic setup. This is suitable for testing and prototyping. ```typescript import { Module } from '@nestjs/common'; import { EventSourcingModule } from '@ocoda/event-sourcing'; import { AccountOpenedEvent, AccountCreditedEvent, AccountDebitedEvent, AccountClosedEvent, } from './app.providers'; @Module({ imports: [ EventSourcingModule.forRoot({ events: [ AccountOpenedEvent, AccountCreditedEvent, AccountDebitedEvent, AccountClosedEvent, ], }), ], }) export class AppModule {} ``` -------------------------------- ### Install PostgreSQL Integration with pnpm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the PostgreSQL integration library using pnpm. ```bash pnpm add @ocoda/event-sourcing-postgres ``` -------------------------------- ### Install MongoDB Integration with pnpm Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/install.mdx Install the MongoDB integration library using pnpm. ```bash pnpm add @ocoda/event-sourcing-mongodb ``` -------------------------------- ### Start Local Docker Services Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Command to start specific Docker Compose services locally. Use this for setting up database environments for testing. ```bash docker compose up -d postgres mariadb ``` -------------------------------- ### Prerequisites for Development Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Specifies the required Node.js and pnpm versions for development. Ensure these versions are installed before proceeding. ```shell node: "^>=20.0.0" pnpm: "^10.4.0" # otherwise, your build will fail ``` -------------------------------- ### Idempotent Command Handler Example Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/commands.mdx Ensure CommandHandlers are idempotent to handle retries robustly. This example prevents creating an account if one already exists for the owner. ```typescript async execute(command: OpenAccountCommand): Promise { if (await this.accountRepository.exists(command.accountOwner)) { throw new Error('Account already exists'); } const accountId = AccountId.generate(); const account = Account.open(accountId, command.accountOwnerIds?.map(AccountOwnerId.from)); await this.accountRepository.save(account); return accountId.value; } ``` -------------------------------- ### Format Account Name Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/value-objects.mdx Example of encapsulating domain-specific logic, such as formatting, within a Value Object. ```typescript public formatName() { return this.props.value.toUpperCase(); } ``` -------------------------------- ### Validate Account Name Length Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/value-objects.mdx Example of validation logic within a Value Object's factory method to ensure data integrity upon creation. ```typescript public static fromString(name: string) { if(name.length < 3) { throw new Error('Account name should contain at least 3 characters'); } return new AccountName({ value: name }); } ``` -------------------------------- ### Clone Your Forked Repository Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Clones your personal fork of the @ocoda/event-sourcing repository to your local machine. This is the first step after forking. ```bash git clone https://github.com/@ocoda/event-sourcing.git ``` -------------------------------- ### Implement Custom Snapshot Store Class Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/custom-stores.mdx Create a class that extends `SnapshotStore` and implements methods for connecting, disconnecting, managing collections, and retrieving snapshots. ```typescript import { SnapshotStore } from '@ocoda/event-sourcing'; import type { FooSnapshotStoreConfig } from './foo-snapshot-store-config'; export class FooSnapshotStore extends SnapshotStore { public async connect(): Promise { // Connect to your Snapshot Store } public async disconnect(): Promise { // Disconnect from your Snapshot Store } public async ensureCollection(pool?: ISnapshotPool): Promise { // Ensure the collection exists in your Snapshot Store } public async *listCollections(filter?: ISnapshotCollectionFilter): AsyncGenerator { // List all collections in your Snapshot Store } await getEvent({ streamId }: SnapshotStream, version: number, pool?: ISnapshotPool): Promise> { // Get a snapshot from your Snapshot Store } ... // The other methods } ``` -------------------------------- ### Supported Database Versions Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Lists the pinned Docker image versions for databases used in integration tests. These ensure stable CI and local runs. ```text Postgres: postgres:14 MongoDB: mongo:8 MariaDB: mariadb:10.11 DynamoDB Local: amazon/dynamodb-local:1.15.0 ``` -------------------------------- ### Implement Custom Event Store Class Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/custom-stores.mdx Create a class that extends `EventStore` and implements the necessary methods for connecting, disconnecting, managing collections, and retrieving events. ```typescript import { EventStore } from '@ocoda/event-sourcing'; import type { FooEventStoreConfig } from './foo-event-store-config'; export class FooEventStore extends EventStore { public async connect(): Promise { // Connect to your Event Store } public async disconnect(): Promise { // Disconnect from your Event Store } public async ensureCollection(pool?: IEventPool): Promise { // Ensure the collection exists in your Event Store } public async *listCollections(filter?: IEventCollectionFilter): AsyncGenerator { // List all collections in your Event Store } await getEvent({ streamId }: EventStream, version: number, pool?: IEventPool): Promise { // Get an event from your Event Store } ... // The other methods } ``` -------------------------------- ### Create Snapshot Store Configuration Interface Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/custom-stores.mdx Define a configuration interface for your custom Snapshot Store, extending `SnapshotStoreConfig` and specifying the driver and any necessary initialization parameters. ```typescript import type { Type } from '@nestjs/common'; import type { SnapshotStoreConfig } from '@ocoda/event-sourcing'; export interface FooSnapshotStoreConfig extends SnapshotStoreConfig { driver: Type; // Add any additional parameters you need to initialize your Snapshot Store // e.g. a connection string } ``` -------------------------------- ### Create Tenant Pools Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/multitenancy.mdx Use the `ensureCollection` method on the eventStore and snapshotStore to create a new pool for a specific tenant. The pool will be created if it doesn't already exist. ```typescript await eventStore.ensureCollection('tenant-1'); await snapshotStore.ensureCollection('tenant-1'); ``` -------------------------------- ### Create Event Store Configuration Interface Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/custom-stores.mdx Define a configuration interface that extends `EventStoreConfig` and specifies your custom Event Store driver and any additional initialization parameters. ```typescript import type { Type } from '@nestjs/common'; import type { EventStoreConfig } from '@ocoda/event-sourcing'; import type { FooEventStore } from './foo.event-store'; export interface FooEventStoreConfig extends EventStoreConfig { driver: Type; // Add any additional parameters you need to initialize your Event Store // e.g. a connection string } ``` -------------------------------- ### Create a Command Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/commands.mdx Commands represent user or system intent to perform an action. Implement the ICommand interface and define properties to carry necessary data. ```typescript import { ICommand } from '@ocoda/event-sourcing'; export class CreditAccountCommand implements ICommand { constructor( public readonly accountId: string, public readonly amount: number, ) {} } ``` -------------------------------- ### Run Lint and Format Checks Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Executes linting and formatting commands using pnpm. This ensures code style consistency and passes CI checks. ```bash pnpm lint format ``` -------------------------------- ### Create Account Snapshot Repository Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/snapshots.mdx Extends `SnapshotRepository` to handle serialization and deserialization of `Account` aggregates. The `@Snapshot` decorator configures the snapshot name and interval. ```typescript import { SnapshotRepository, Snapshot } from '@ocoda/event-sourcing'; @Snapshot(Account, { name: 'account', interval: 5 }) export class AccountSnapshotRepository extends SnapshotRepository { serialize({ id, ownerIds, balance, openedOn, closedOn }: Account): ISnapshot { return { id: id.value, ownerIds: ownerIds.map(({ value }) => value), balance, openedOn: openedOn ? openedOn.toISOString() : undefined, closedOn: closedOn ? closedOn.toISOString() : undefined, }; } deserialize({ id, ownerIds, balance, openedOn, closedOn }: ISnapshot): Account { const account = new Account(); account.id = AccountId.from(id); account.ownerIds = ownerIds.map(AccountOwnerId.from); account.balance = balance; account.openedOn = openedOn && new Date(openedOn); account.closedOn = closedOn && new Date(closedOn); return account; } } ``` -------------------------------- ### Generate a Changeset Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Creates a changeset file to document changes for versioning and changelog generation. This is important for core and integration package updates. ```bash pnpm exec changeset ``` -------------------------------- ### Create a Command Handler Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/commands.mdx Command handlers execute command logic, interacting with aggregates and repositories. They are decorated with @CommandHandler and implement ICommandHandler. ```typescript import { CommandHandler, type ICommandHandler } from '@ocoda/event-sourcing'; @CommandHandler(CreditAccountCommand) export class CreditAccountCommandHandler implements ICommandHandler { constructor(private readonly accountRepository: AccountRepository) {} await account.credit(command.amount); await this.accountRepository.save(account); } } ``` -------------------------------- ### Define and Serialize Custom Events Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/event-serialization.mdx Defines an 'AccountOpenedEvent' and its serializer. Use this pattern to create custom event types and specify how they should be serialized and deserialized. ```typescript import { Callout } from 'nextra/components' @Event('account-opened') export class AccountOpenedEvent implements IEvent { constructor( public readonly accountId: AccountId, public readonly openedOn: Date, public readonly accountOwnerIds?: AccountOwnerId[] ) {} } @EventSerializer(AccountOpenedEvent) export class AccountOpenedEventSerializer implements IEventSerializer { serialize({ accountId, openedOn, accountOwnerIds }: AccountOpenedEvent): IEventPayload { return { accountId: accountId.value, openedOn: openedOn.toISOString(), accountOwnerIds: accountOwnerIds?.map((id) => id.value) }; } deserialize({ id, openedOn, accountOwnerIds }: IEventPayload): AccountOpenedEvent { const accountId = AccountId.from(id); const openedOnDate = openedOn && new Date(openedOn); const ownerIds = accountOwnerIds?.map((id) => AccountOwnerId.from(id)); return new AccountOpenedEvent(accountId, openedOnDate, ownerIds); } } ``` -------------------------------- ### Compare Value Objects for Equality Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/value-objects.mdx Demonstrates how to compare two Value Objects for equality using the built-in 'equals' method, which checks property values. ```typescript const name1 = AccountName.fromString('John Doe'); const name2 = AccountName.fromString('John Doe'); name1.equals(name2); // true ``` -------------------------------- ### Create Banking Production Scenario Source: https://github.com/ocoda/event-sourcing/blob/master/packages/testing/README.md Generates a production-style banking event scenario for E2E testing. Use this to inspect all generated events or feed them into a data store for validation. ```typescript import { createBankingProductionScenario } from '@ocoda/event-sourcing-testing/e2e'; const scenario = createBankingProductionScenario(); // Inspect all events or feed into a store for (const event of scenario.allEvents) { console.log(event.event, event.metadata.aggregateId); } ``` -------------------------------- ### Define Account Aggregate Repository Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/repositories.mdx This TypeScript snippet shows how to create an AccountRepository to load and save Account aggregates using an EventStore. It demonstrates constructor injection of the EventStore and methods for retrieving by ID and saving changes. ```typescript import { Injectable } from '@nestjs/common'; import { EventStore, EventStream } from '@ocoda/event-sourcing'; import { Account, AccountId } from '../../domain/models'; @Injectable() export class AccountRepository { constructor(private readonly eventStore: EventStore) {} async getById(accountId: AccountId): Promise { const eventStream = EventStream.for(Account, accountId); const account = new Account(); const eventCursor = this.eventStore.getEvents(eventStream, { fromVersion: account.version + 1 }); await account.loadFromHistory(eventCursor); return account; } async save(account: Account): Promise { const events = account.commit(); const stream = EventStream.for(Account, account.id); await this.eventStore.appendEvents(stream, account.version, events); } } ``` -------------------------------- ### Register Custom Snapshot Store in Module Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/custom-stores.mdx Register your custom Snapshot Store within the `EventSourcingModule` using `forRootAsync`, providing its configuration alongside the Event Store configuration. ```typescript import { Module } from '@nestjs/common'; import { EventSourcingModule } from '@ocoda/event-sourcing'; import { FooSnapshotStore } from './foo.snapshot-store'; import type { FooSnapshotStoreConfig } from './foo-snapshot-store-config'; @Module({ imports: [ EventSourcingModule.forRootAsync({ useFactory: () => ({ events: [...Events], eventStore: { ... }, snapshotStore: { driver: FooSnapshotStore, ... // the additional parameters from your configuration } }), }), ], providers: [...], controllers: [...], }) class AppModule {} ``` -------------------------------- ### Commit Changes with a Message Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Commits your staged changes with a clear and concise message. Follow conventional commit guidelines if applicable. ```bash git commit -m "Add description of your changes" ``` -------------------------------- ### Interact with Event Store and Snapshot Repository using Pools Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/multitenancy.mdx When performing operations like appending or retrieving events, or saving/loading snapshots, specify the tenant's pool name to ensure data is associated with the correct tenant. ```typescript await this.eventStore.appendEvents(stream, account.version, events, 'tenant-1'); await this.eventStore.getEvents(eventStream, { fromVersion: account.version + 1, pool: 'tenant-1' }); await this.accountSnapshotRepository.save(accountId, account, 'tenant-1'); await this.accountSnapshotRepository.load(accountId, 'tenant-1'); ``` -------------------------------- ### Integrate Snapshot Repository into Aggregate Repository Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/snapshots.mdx Injects `AccountSnapshotRepository` into `AccountRepository` to optimize aggregate loading by first attempting to load from a snapshot. ```typescript @Injectable() export class AccountRepository { constructor( private readonly eventStore: EventStore, private readonly accountSnapshotRepository: AccountSnapshotRepository, ) {} async getById(accountId: AccountId) { const eventStream = EventStream.for(Account, accountId); const account = await this.accountSnapshotRepository.load(accountId); const events = this.eventStore.getEvents(eventStream, { fromVersion: account.version + 1 }); await account.loadFromHistory(events); if (account.version < 1) { throw new AccountNotFoundException(accountId.value); } return account; } async getByIds(accountIds: AccountId[]) { const accounts = await this.accountSnapshotRepository.loadMany(accountIds, 'e2e'); for (const account of accounts) { const eventStream = EventStream.for(Account, account.id); const eventCursor = this.eventStore.getEvents(eventStream, { pool: 'e2e', fromVersion: account.version + 1 }); await account.loadFromHistory(eventCursor); } return accounts; } async save(account: Account): Promise { const events = account.commit(); const stream = EventStream.for(Account, account.id); // Append the events to the event store await this.eventStore.appendEvents(stream, account.version, events); // Save a snapshot of the account await this.accountSnapshotRepository.save(account.id, account); } } ``` -------------------------------- ### Register Custom Event Store in Module Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/custom-stores.mdx Register your custom Event Store within the `EventSourcingModule` using `forRootAsync`, providing its configuration via a factory function. ```typescript import { Module } from '@nestjs/common'; import { EventSourcingModule } from '@ocoda/event-sourcing'; import { FooEventStore } from './foo.event-store'; import type { FooEventStoreConfig } from './foo-event-store-config'; @Module({ imports: [ EventSourcingModule.forRootAsync({ useFactory: () => ({ events: [...Events], eventStore: { driver: FooEventStore, ... // the additional parameters from your configuration } }), }), ], providers: [...], controllers: [...], }) class AppModule {} ``` -------------------------------- ### Push Changes to Your Fork Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Pushes your local feature branch to your forked repository on GitHub. This makes your changes available for a pull request. ```bash git push origin feature/your-feature-name ``` -------------------------------- ### Register a Custom Event Publisher Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/event-pubsub.mdx Register a CustomEventPublisher to push EventEnvelopes to external systems like Redis, SNS, or Kafka. This adds an additional publisher to the EventBus. ```typescript import { EventPublisher, EventEnvelope, IEventPublisher, IEvent } from "@ocoda/event-sourcing"; @EventPublisher() export class CustomEventPublisher implements IEventPublisher { async publish(envelope: EventEnvelope): Promise { // Publish the event to an external system } } ``` -------------------------------- ### Create an AccountName Value Object Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/value-objects.mdx Extends the ValueObject class to create a custom Value Object. Includes a static factory method for creation with validation. ```typescript import { ValueObject } from '@ocoda/event-sourcing'; export class AccountName extends ValueObject { public static fromString(name: string) { if(name.length < 3) { throw new Error('Account name should contain at least 3 characters'); } return new Accountname({ value: name }); } get value(): string { return this.props.value; } } ``` -------------------------------- ### Create a New Feature Branch Source: https://github.com/ocoda/event-sourcing/blob/master/CONTRIBUTING.md Creates a new Git branch for your feature or bug fix. It's recommended to name branches descriptively. ```bash git checkout -b feature/your-feature-name ``` -------------------------------- ### Create a Snapshot Stream for an Aggregate Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/under-the-hood/streams.mdx Use `SnapshotStream.for` to create a stream for storing snapshots of a specific aggregate type and ID. This method also maintains a reference to the aggregate name for the snapshot store. ```typescript import { SnapshotStream } from '@ocoda/event-sourcing'; const stream = SnapshotStream.for(Account, accountId); console.log(snapshotStream.aggregate); // account console.log(snapshotStream.aggregateId); // d46fb0f9-02dc-4d11-a282-ab00f7fffeff console.log(snapshotStream.streamId); // account-d46fb0f9-02dc-4d11-a282-ab00f7fffeff ``` -------------------------------- ### Register Command Handler in NestJS Module Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/commands.mdx Register your CommandHandlers as providers in your NestJS module to enable framework discovery and execution. ```typescript import { Module } from '@nestjs/common'; @Module({ providers: [CreditAccountCommandHandler, AccountRepository], }) export class AccountModule {} ``` -------------------------------- ### Create an Event with @Event Decorator Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/events.mdx Define an event class by implementing IEvent and applying the @Event() decorator. The decorator can optionally specify an internal event name. ```typescript import { IEvent } from '@ocoda/event-sourcing'; @Event('account-opened') export class AccountOpenedEvent implements IEvent { constructor( public readonly accountId: string, public readonly openedOn: string, public readonly accountOwnerIds?: string[], ) {} } ``` -------------------------------- ### Register an Event Subscriber Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/event-pubsub.mdx Register an EventSubscriber to listen for specific events like AccountOpenedEvent. The handle method is called when the event is published. ```typescript import { EventSubscriber, EventEnvelope, IEventSubscriber } from "@ocoda/event-sourcing"; import { AccountOpenedEvent } from "./account-opened.event"; @EventSubscriber(AccountOpenedEvent) export class AccountOpenedEventSubscriber implements IEventSubscriber { handle(envelope: EventEnvelope) { // Handle the AccountOpenedEvent } } ``` -------------------------------- ### Register Repository in NestJS Module Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/repositories.mdx This TypeScript snippet demonstrates how to register the AccountRepository as a provider within a NestJS module. This makes the repository available for dependency injection throughout the application. ```typescript import { Module } from '@nestjs/common'; @Module({ providers: [AccountRepository] }) export class AccountModule {} ``` -------------------------------- ### Disable Default Pool in Store Configuration Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/advanced/multitenancy.mdx Configure the EventSourcingModule to disable the creation of a default pool for both event and snapshot stores. This is the first step to enable multitenancy. ```typescript @Module({ imports: [ EventSourcingModule.forRoot({ events: Events, eventStore: { driver: InMemoryEventStore, useDefaultPool: false, }, snapshotStore: { driver: InMemorySnapshotStore, useDefaultPool: false, }, }), ] }) export class AppModule {} ``` -------------------------------- ### Create an Event Stream for an Aggregate Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/under-the-hood/streams.mdx Use `EventStream.for` to create a stream for a specific aggregate type and ID. The stream name prefix is derived from the @Aggregate decorator. ```typescript import { EventStream } from '@ocoda/event-sourcing'; const stream = EventStream.for(Account, accountId); console.log(eventStream.aggregateId); // d46fb0f9-02dc-4d11-a282-ab00f7fffeff console.log(eventStream.streamId); // account-d46fb0f9-02dc-4d11-a282-ab00f7fffeff ``` -------------------------------- ### Define an Aggregate Root Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/aggregates.mdx Create an aggregate by inheriting from AggregateRoot and applying the @Aggregate decorator. The decorator can optionally specify the stream name for events and snapshots. ```typescript import { Aggregate, AggregateRoot } from '@ocoda/event-sourcing'; @Aggregate('account') class Account extends AggregateRoot { ... } ``` -------------------------------- ### Enforce Business Invariants Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/aggregates.mdx Implement business rules within the aggregate to maintain consistency. Throw an error if a rule is violated before applying an event. ```typescript public debit(amount: number) { if (this.balance - amount < 0) { throw new Error('Insufficient funds'); } this.applyEvent(new AccountDebitedEvent(amount)); } ``` -------------------------------- ### Handle Events to Update State Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/aggregates.mdx Define methods decorated with @EventHandler to update the aggregate's state when specific events are applied. This decouples state mutation from business logic. ```typescript @EventHandler(AccountCreditedEvent) applyAccountCreditedEvent(event: AccountCreditedEvent) { this.balance += event.amount; } ``` -------------------------------- ### Idempotent Event Handler for Adding Account Owner Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/aggregates.mdx This TypeScript code snippet demonstrates an idempotent event handler for an `AccountOwnerAddedEvent`. It checks if the owner ID already exists before adding it to prevent duplicates when events are replayed. ```typescript onAccountOwnerAddedEvent(event: AccountOwnerAddedEvent) { if (!this.ownerIds.find(({ value }) => value === event.accountOwnerId)) { this.ownerIds.push(AccountOwnerId.from(event.accountOwnerId)); } } ``` -------------------------------- ### Register an Event Handler with @EventHandler Decorator Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/events.mdx Register a method within an aggregate to handle a specific event by decorating it with @EventHandler(). This method will be called when the event is applied to the aggregate. ```typescript import { EventHandler } from '@ocoda/event-sourcing'; export class Account { constructor() { this.balance = 0; } public credit(amount: number) { this.applyEvent(new AccountCreditedEvent(amount)); } @EventHandler(AccountCreditedEvent) applyAccountCreditedEvent(event: AccountCreditedEvent) { this.balance += event.amount; } } ``` -------------------------------- ### Mutate State Through Events Source: https://github.com/ocoda/event-sourcing/blob/master/docs/pages/start/aggregates.mdx Encapsulate state changes within events by using the applyEvent method. This ensures state changes are tracked and can be replayed. ```typescript public credit(amount: number) { this.applyEvent(new AccountCreditedEvent(amount)); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.