### Install Event Nest PostgreSQL Package Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Install the core and PostgreSQL packages for Event Nest using npm. ```bash npm install --save @event-nest/core @event-nest/postgresql ``` -------------------------------- ### Install Dependencies with pnpm Source: https://github.com/nicktsitlakidis/event-nest/blob/main/AGENTS.md Installs all project dependencies using the pnpm package manager. This is a prerequisite for running any other commands. ```bash pnpm install ``` -------------------------------- ### Install Event Nest MongoDB Package Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Install the core and MongoDB packages for Event Nest using npm. ```bash npm install --save @event-nest/core @event-nest/mongodb ``` -------------------------------- ### PostgreSQL Configuration with Snapshots Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Configure the module to use snapshots for performance. This example includes a snapshot table name and a count-based snapshot strategy. ```typescript import { ForCountSnapshotStrategy } from "@event-nest/core"; EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:pass@localhost:5432/event-store", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", snapshotTableName: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }), ensureTablesExist: true }) ``` -------------------------------- ### AllOfSnapshotStrategy Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Combine multiple snapshot strategies using AND logic. A snapshot is created only if all constituent strategies return true. ```typescript // Create snapshots only for User aggregates AND after 10 events new AllOfSnapshotStrategy([ new ForAggregateRootsStrategy({ aggregates: [User] }), new ForCountSnapshotStrategy({ count: 10 }) ]) ``` -------------------------------- ### AnyOfSnapshotStrategy Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Combine multiple snapshot strategies using OR logic. A snapshot is created if any of the constituent strategies return true. ```typescript // Create snapshots either: // - When a major event occurs, OR // - After 20 events new AnyOfSnapshotStrategy([ new ForCountSnapshotStrategy({ count: 20 }), new ForEventsSnapshotStrategy({ eventClasses: [AccountClearedEvent] }) ]) ``` -------------------------------- ### UserEventSubscription Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/subscriptions.md An example of a service implementing the OnDomainEvent interface to handle UserCreatedEvent. It updates a read model with user data. ```typescript @Injectable() @DomainEventSubscription(UserCreatedEvent) export class UserEventSubscription implements OnDomainEvent { constructor(private readModelService: UserReadModelService) {} async onDomainEvent(event: PublishedDomainEvent): Promise { await this.readModelService.createUser({ id: event.aggregateRootId, name: event.payload.name, email: event.payload.email }); } } ``` -------------------------------- ### DomainEventSubscription Full Example with Imports Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/decorators.md A complete example of a NestJS service subscribed to UserCreatedEvent and UserUpdatedEvent, demonstrating asynchronous handling and interaction with a read model service. Events are persisted even if subscriptions fail. ```typescript import { DomainEventSubscription, OnDomainEvent, PublishedDomainEvent } from "@event-nest/core"; import { Injectable } from "@nestjs/common"; @Injectable() @DomainEventSubscription(UserCreatedEvent, UserUpdatedEvent) export class UserEventSubscription implements OnDomainEvent { constructor(private readModelService: ReadModelService) {} async onDomainEvent(event: PublishedDomainEvent): Promise { if (event.payload instanceof UserCreatedEvent) { await this.readModelService.createUser(event); } else if (event.payload instanceof UserUpdatedEvent) { await this.readModelService.updateUser(event); } } } ``` -------------------------------- ### Service to Update User with Snapshotting Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md An example service method demonstrating how to update a user aggregate. It finds the aggregate with its snapshot, applies changes, and commits them. If the snapshot strategy is met, a new snapshot is automatically created. ```typescript async updateUser(id: string, newName: string) { const { events, snapshot } = await eventStore.findWithSnapshot(User, id); const user = User.fromEvents(id, events, snapshot); const userWithPublisher = eventStore.addPublisher(user); user.update(newName); // If snapshot strategy activates, a snapshot is automatically created on commit await userWithPublisher.commit(); } ``` -------------------------------- ### ForAggregateRootsStrategy Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Create snapshots only for specified aggregate root types. This allows fine-grained control over which aggregates benefit from snapshotting. ```typescript // Create snapshots only for User and Order aggregates new ForAggregateRootsStrategy({ aggregates: [User, Order] }) ``` -------------------------------- ### ApplyEvent Decorator Examples Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/decorators.md Examples of using the @ApplyEvent decorator to handle UserCreatedEvent and UserUpdatedEvent. ```typescript @ApplyEvent(UserCreatedEvent) private applyUserCreatedEvent(event: UserCreatedEvent) { this.name = event.name; this.email = event.email; } @ApplyEvent(UserUpdatedEvent) private applyUserUpdatedEvent(event: UserUpdatedEvent) { this.name = event.newName; } ``` -------------------------------- ### PostgreSQL Configuration with Connection Pool Tuning Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Tune the PostgreSQL connection pool settings for optimal performance. This example sets minimum and maximum connections, along with various timeout values. ```typescript EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:pass@localhost:5432/event-store", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true, connectionPool: { min: 2, max: 10, idleTimeoutMillis: 30000, acquireTimeoutMillis: 5000, createTimeoutMillis: 5000 } }) ``` -------------------------------- ### ForCountSnapshotStrategy Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Create snapshots at a specific event count interval. This strategy is useful for maintaining performance by periodically saving aggregate state. ```typescript // Create snapshots every 10 events new ForCountSnapshotStrategy({ count: 10 }) // Aggregate with version 0: append 10 events → snapshot created at version 10 // Aggregate with version 10: append 10 events → snapshot created at version 20 ``` -------------------------------- ### ForEventsSnapshotStrategy Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Create snapshots whenever specific event types are committed. This strategy is ideal for capturing significant state changes triggered by particular events. ```typescript // Create snapshots when user makes a major purchase new ForEventsSnapshotStrategy({ eventClasses: [PremiumMembershipPurchasedEvent, HighValueOrderPlacedEvent] }) ``` -------------------------------- ### Production PostgreSQL Configuration with Environment Variables Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Configure the PostgreSQL module for production using environment variables for sensitive information like the database URL. This example uses `forRootAsync` and `ConfigModule`. ```typescript @Module({ imports: [ ConfigModule.forRoot(), EventNestPostgreSQLModule.forRootAsync({ inject: [ConfigService], useFactory: (configService: ConfigService): PostgreSQLModuleOptions => { return { connectionUri: configService.getOrThrow("DATABASE_URL"), schemaName: configService.get("DB_SCHEMA", "event_nest"), aggregatesTableName: "aggregates", eventsTableName: "events", snapshotTableName: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 20 }), ensureTablesExist: false, connectionPool: { min: 5, max: 20, idleTimeoutMillis: 30000 } }; } }) ] }) export class AppModule {} ``` -------------------------------- ### NestJS Service Usage of EventStore Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Example of injecting and using the EventStore in a NestJS service to create and update orders. Ensure the EVENT_STORE provider is configured. ```typescript import { Injectable, Inject } from "@nestjs/common"; import { EVENT_STORE, EventStore } from "@event-nest/core"; @Injectable() export class OrderService { constructor( @Inject(EVENT_STORE) private eventStore: EventStore ) {} async createOrder(userId: string, items: OrderItem[]): Promise { const orderId = await this.eventStore.generateEntityId(); const order = Order.createNew(orderId, userId, items); const orderWithPublisher = this.eventStore.addPublisher(order); await orderWithPublisher.commit(); return orderId; } async updateOrderStatus(orderId: string, newStatus: string): Promise { const { events, snapshot } = await this.eventStore.findWithSnapshot(Order, orderId); const order = Order.fromEvents(orderId, events, snapshot); const orderWithPublisher = this.eventStore.addPublisher(order); order.updateStatus(newStatus); await orderWithPublisher.commit(); } } ``` -------------------------------- ### DomainEventSubscription Configuration Signature Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/decorators.md Registers a NestJS service to listen to domain events using a configuration object, allowing control over asynchronous behavior. If isAsync is false, the commit waits for subscription completion. ```typescript @DomainEventSubscription({ eventClasses: [UserCreatedEvent, UserUpdatedEvent], isAsync: false }) export class SyncUserEventSubscription implements OnDomainEvent { onDomainEvent(event: PublishedDomainEvent): Promise { // ... } } ``` -------------------------------- ### Published Domain Event Payload Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/types.md Illustrates the structure of the PublishedDomainEvent object received by subscribers. Shows the eventId, aggregateRootId, payload, occurredAt, and version properties. ```typescript { eventId: "event-uuid", aggregateRootId: "agg-id", payload: T, // Fully deserialized event object occurredAt: Date, version: number // Aggregate version when event was committed } ``` -------------------------------- ### DomainEventSubscription Variadic Signature Example Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/decorators.md Registers a NestJS service to listen to multiple domain events using the variadic signature. Events are processed asynchronously by default. ```typescript @DomainEventSubscription(UserCreatedEvent, UserUpdatedEvent) export class UserEventSubscription implements OnDomainEvent { onDomainEvent(event: PublishedDomainEvent): Promise { // ... } } ``` -------------------------------- ### Deserialize Object to Class Instance Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/types.md Example function demonstrating the usage of the Class type to deserialize a plain object into an instance of a specific class. Requires a 'deserialize' function, likely from a library like 'class-transformer'. ```typescript function deserialize(cls: Class, data: object): T { return plainToClass(cls, data); } ``` -------------------------------- ### Asynchronous MongoDB Module Configuration with Environment Variables Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Demonstrates asynchronous configuration of the MongoDB module using environment variables and a factory function. ```typescript @Module({ imports: [ ConfigModule.forRoot(), EventNestMongoDbModule.forRootAsync({ inject: [ConfigService], useFactory: (configService: ConfigService): MongodbModuleOptions => { const mongoUri = configService.get("MONGODB_URI"); const env = configService.get("NODE_ENV"); return { connectionUri: mongoUri, aggregatesCollection: `aggregates-${env}`, eventsCollection: `events-${env}`, snapshotCollection: env === "production" ? `snapshots-${env}` : undefined, snapshotStrategy: env === "production" ? new ForCountSnapshotStrategy({ count: 10 }) : undefined }; } }) ] }) export class AppModule {} ``` -------------------------------- ### Discover Projects with Nx Source: https://github.com/nicktsitlakidis/event-nest/blob/main/AGENTS.md Lists all available projects within the Nx monorepo. Useful for understanding the repository's structure. ```bash pnpm nx show projects ``` -------------------------------- ### Incrementing snapshotRevision for Removed Fields Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/exceptions.md Example of incrementing snapshotRevision when removing fields from a snapshot. ```typescript @AggregateRootConfig({ name: "User", snapshotRevision: 3 }) ``` -------------------------------- ### Incrementing snapshotRevision for New Fields Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/exceptions.md Example of incrementing snapshotRevision when adding new fields to a snapshot. ```typescript // v1: { name, email } // v2: { name, email, role } @AggregateRootConfig({ name: "User", snapshotRevision: 2 }) ``` -------------------------------- ### Minimal PostgreSQL Configuration Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Use this basic configuration when snapshotting is not required. Ensure all necessary table names and the connection URI are provided. ```typescript EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:pass@localhost:5432/event-store", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true }) ``` -------------------------------- ### Project File Structure Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/README.md Lists the main files and their organization within the Event Nest project's output directory. ```text /workspace/home/output/ ├── README.md # This file ├── INDEX.md # Navigation and overview ├── aggregate-root.md # AggregateRoot class documentation ├── decorators.md # Decorator reference ├── event-store.md # EventStore interface ├── exceptions.md # Exception reference ├── mongodb-module.md # MongoDB integration ├── postgresql-module.md # PostgreSQL integration ├── quick-reference.md # Quick patterns and signatures ├── snapshots.md # Snapshot optimization ├── stored-event.md # StoredEvent class ├── subscriptions.md # Event subscriptions └── types.md # Type definitions ``` -------------------------------- ### MongoDB Events Collection Schema Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Example document structure for the 'events' collection, which stores all domain events. ```json { "_id": "event-uuid", "aggregateRootId": "user-123", "aggregateRootVersion": 1, "aggregateRootName": "User", "eventName": "user-created-event", "payload": { "name": "John", "email": "john@example.com" }, "createdAt": ISODate("2024-01-01T00:00:00Z") } ``` -------------------------------- ### Incrementing snapshotRevision for Changed Field Types/Semantics Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/exceptions.md Example of incrementing snapshotRevision when changing field types or semantics in a snapshot. ```typescript @AggregateRootConfig({ name: "User", snapshotRevision: 4 }) ``` -------------------------------- ### Run Linting Source: https://github.com/nicktsitlakidis/event-nest/blob/main/CONTRIBUTING.md Check code style and identify potential issues across the project using ESLint and Prettier. ```bash pnpm lint ``` -------------------------------- ### Run Tests for All Libraries Source: https://github.com/nicktsitlakidis/event-nest/blob/main/CONTRIBUTING.md Execute all unit and integration tests across all libraries in the monorepo. ```bash pnpm test ``` -------------------------------- ### MongoDB Aggregates Collection Schema Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Example document structure for the 'aggregates' collection, which stores the current version of each aggregate root. ```json { "_id": "user-123", "version": 5 } ``` -------------------------------- ### Configuration with Snapshots Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/INDEX.md Includes options for configuring snapshots in Event Nest, specifying collection/table names and the snapshot strategy. ```typescript { // ... other options ... snapshotCollection: "snapshots", // MongoDB snapshotTableName: "snapshots", // PostgreSQL snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) } ``` -------------------------------- ### MongoDB Snapshots Collection Schema (Optional) Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Example document structure for the optional 'snapshots' collection, which stores snapshots of aggregate state. ```json { "_id": "snapshot-uuid", "aggregateRootId": "user-123", "aggregateRootVersion": 10, "revision": 1, "payload": { "name": "John Updated", "email": "john@example.com" } } ``` -------------------------------- ### Build Specific Library Source: https://github.com/nicktsitlakidis/event-nest/blob/main/CONTRIBUTING.md Compile and build a specific library within the monorepo. Replace 'core' with the desired library name. ```bash nx build core ``` -------------------------------- ### EventNestPostgreSQLModule.forRoot Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Registers the PostgreSQL module globally with synchronous configuration. This makes exported providers available application-wide. ```APIDOC ## EventNestPostgreSQLModule.forRoot ### Description Registers the PostgreSQL module globally. Exported providers are available application-wide without additional imports. ### Method `static forRoot(options: PostgreSQLModuleOptions): DynamicModule` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **options** (PostgreSQLModuleOptions) - Required - Module configuration ### Request Example ```typescript import { Module } from "@nestjs/common"; import { EventNestPostgreSQLModule } from "@event-nest/postgresql"; import { ForCountSnapshotStrategy } from "@event-nest/core"; @Module({ imports: [ EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:password@localhost:5432/event-store", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true, snapshotTableName: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) }) ] }) export class AppModule {} ``` ### Response #### Success Response (200) `DynamicModule` #### Response Example None provided in source. ``` -------------------------------- ### Configure Event Nest with PostgreSQL and Snapshots Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Configure EventNestPostgreSQLModule with snapshotting enabled. Provide snapshot table name and strategy. ```typescript import { ForCountSnapshotStrategy } from "@event-nest/core"; import { EventNestPostgreSQLModule } from "@event-nest/postgresql"; import { Module } from "@nestjs/common"; @Module({ imports: [ EventNestPostgreSQLModule.forRoot({ aggregatesTableName: "aggregates", connectionUri: "postgresql://postgres:password@localhost:5432/event_nest", eventsTableName: "events", schemaName: "event_nest_schema", ensureTablesExist: true, snapshotTableName: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) }) ] }) export class AppModule {} ``` -------------------------------- ### Deserialize Stored Event Payload Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/stored-event.md Example of retrieving stored events and deserializing the payload of the first event into a specific event class instance. ```typescript // Event retrieved from storage const storedEvent = await eventStore.findByAggregateRootId(User, userId); // Get the first event and deserialize it const firstEvent = storedEvent[0]; const userCreatedEvent = firstEvent.getPayloadAs(UserCreatedEvent); console.log(userCreatedEvent.name); // "John" console.log(userCreatedEvent.email); // "john@example.com" ``` -------------------------------- ### Apply Event Handler in Aggregate Root Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/stored-event.md An example of an event handler within an aggregate root class that receives a fully typed and deserialized event. ```typescript @ApplyEvent(UserCreatedEvent) private applyUserCreatedEvent(event: UserCreatedEvent) { // event is now fully typed and deserialized this.name = event.name; this.email = event.email; } ``` -------------------------------- ### Event Nest Project File Structure Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/INDEX.md Overview of the directory structure for the Event Nest project, detailing the location of various documentation files. ```text /workspace/home/output/ ├── INDEX.md # This file ├── aggregate-root.md # AggregateRoot class ├── decorators.md # All decorators ├── event-store.md # EventStore interface ├── stored-event.md # StoredEvent class ├── snapshots.md # Snapshot support ├── subscriptions.md # Event subscriptions ├── mongodb-module.md # MongoDB integration ├── postgresql-module.md # PostgreSQL integration ├── exceptions.md # Exception reference ├── types.md # Type definitions └── quick-reference.md # Quick lookup guide ``` -------------------------------- ### EventNestPostgreSQLModule.registerAsync Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Registers the PostgreSQL module scoped to the importing module with asynchronous configuration. ```APIDOC ## EventNestPostgreSQLModule.registerAsync ### Description Registers the PostgreSQL module scoped to the importing module with asynchronous configuration. ### Method `static registerAsync(options: PostgreSQLModuleAsyncOptions): DynamicModule` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **options** (PostgreSQLModuleAsyncOptions) - Required - Async configuration object ### Request Example None provided in source. ### Response #### Success Response (200) `DynamicModule` #### Response Example None provided in source. ``` -------------------------------- ### Get Payload As Event Class Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/stored-event.md Converts the stored payload to an instance of the specified event class using class-transformer. Ensure the event class uses appropriate class-transformer decorators. ```typescript public getPayloadAs(payloadClass: Class): T ``` -------------------------------- ### Lint Specific Project with Nx Source: https://github.com/nicktsitlakidis/event-nest/blob/main/AGENTS.md Runs the linting process for a specific project within the monorepo. Use this for targeted checks during development. ```bash pnpm nx lint core ``` -------------------------------- ### MongoDB Module Configuration with Multiple Snapshot Strategies Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Configures the MongoDB module with a combination of snapshot strategies using AnyOfSnapshotStrategy. ```typescript import { AnyOfSnapshotStrategy, ForCountSnapshotStrategy, ForEventsSnapshotStrategy } from "@event-nest/core"; EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/event-store", aggregatesCollection: "aggregates", eventsCollection: "events", snapshotCollection: "snapshots", snapshotStrategy: new AnyOfSnapshotStrategy([ new ForCountSnapshotStrategy({ count: 20 }), new ForEventsSnapshotStrategy({ eventClasses: [UserIdentityChangedEvent, OrderCancelledEvent] }) ]), concurrentSubscriptions: true }) ``` -------------------------------- ### Register PostgreSQL Module Asynchronously Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Use `forRootAsync` for global registration with asynchronous configuration, suitable when settings depend on other services. Providers are available application-wide. ```typescript @Module({ imports: [ EventNestPostgreSQLModule.forRootAsync({ inject: [ConfigService], useFactory: async (configService: ConfigService): Promise => { const dbHost = await configService.get("DB_HOST"); const dbPort = await configService.get("DB_PORT"); const dbName = await configService.get("DB_NAME"); const dbUser = await configService.get("DB_USER"); const dbPassword = await configService.get("DB_PASSWORD"); return { connectionUri: `postgresql://${dbUser}:${dbPassword}@${dbHost}:${dbPort}/${dbName}`, schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true }; } }) ] }) export class AppModule {} ``` -------------------------------- ### Assert SnapshotAware Aggregate Root Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Asserts that an aggregate root instance is snapshot-aware and has correct class configuration. Throws specific exceptions if validation fails, such as missing aggregate root name or improper snapshot awareness setup. ```typescript export function assertIsSnapshotAwareAggregateRoot( aggregateRoot: AggregateRoot ): asserts aggregateRoot is SnapshotAwareAggregateRoot ``` ```typescript assertIsSnapshotAwareAggregateRoot(user); // Throws if not properly configured // Type guard: user is now SnapshotAwareAggregateRoot ``` -------------------------------- ### PostgreSQL Configuration with SSL Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Configure SSL for secure connections to the PostgreSQL database. This requires providing the certificate content and setting authorization options. ```typescript import * as fs from "fs"; const certFile = fs.readFileSync("/path/to/certificate.crt", "utf-8"); EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:pass@localhost:5432/event-store", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true, ssl: { certificate: certFile, rejectUnauthorized: true } }) ``` -------------------------------- ### Minimal PostgreSQL Configuration Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/INDEX.md Provides the minimal configuration for the Event Nest PostgreSQL module. Ensure connection URI, schema, and table names are set. ```typescript EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:pass@localhost/db", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events" }) ``` -------------------------------- ### Register PostgreSQL Module Synchronously Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Use `forRoot` to register the PostgreSQL module globally with synchronous configuration options. This makes providers available application-wide. ```typescript import { Module } from "@nestjs/common"; import { EventNestPostgreSQLModule } from "@event-nest/postgresql"; import { ForCountSnapshotStrategy } from "@event-nest/core"; @Module({ imports: [ EventNestPostgreSQLModule.forRoot({ connectionUri: "postgresql://user:password@localhost:5432/event-store", schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true, snapshotTableName: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) }) ] }) export class AppModule {} ``` -------------------------------- ### EventNestPostgreSQLModule.forRootAsync Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Registers the PostgreSQL module globally with asynchronous configuration. This is useful when configuration depends on other services or environment variables. ```APIDOC ## EventNestPostgreSQLModule.forRootAsync ### Description Registers the PostgreSQL module globally with asynchronous configuration. Useful when configuration depends on other services or environment. ### Method `static forRootAsync(options: PostgreSQLModuleAsyncOptions): DynamicModule` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **options** (PostgreSQLModuleAsyncOptions) - Required - Async configuration object ### Configuration Object ```typescript interface PostgreSQLModuleAsyncOptions { inject?: any[]; useFactory: (...parameters: any[]) => PostgreSQLModuleOptions | Promise; } ``` ### Request Example ```typescript @Module({ imports: [ EventNestPostgreSQLModule.forRootAsync({ inject: [ConfigService], useFactory: async (configService: ConfigService): Promise => { const dbHost = await configService.get("DB_HOST"); const dbPort = await configService.get("DB_PORT"); const dbName = await configService.get("DB_NAME"); const dbUser = await configService.get("DB_USER"); const dbPassword = await configService.get("DB_PASSWORD"); return { connectionUri: `postgresql://${dbUser}:${dbPassword}@${dbHost}:${dbPort}/${dbName}`, schemaName: "event_nest", aggregatesTableName: "aggregates", eventsTableName: "events", ensureTablesExist: true }; } }) ] }) export class AppModule {} ``` ### Response #### Success Response (200) `DynamicModule` #### Response Example None provided in source. ``` -------------------------------- ### User Aggregate Root Implementation Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Implements the User aggregate root, extending AggregateRoot and configured with @AggregateRootConfig. It includes static methods for creating new users and reconstituting from events, as well as an update method. Event application methods are decorated with @ApplyEvent. ```typescript import { AggregateRoot, AggregateRootConfig, ApplyEvent, StoredEvent } from "@event-nest/core"; @AggregateRootConfig({ name: "User" }) export class User extends AggregateRoot { private name: string; private email: string; private constructor(id: string) { super(id); } public static createNew(id: string, name: string, email: string): User { const user = new User(id); const event = new UserCreatedEvent(name, email); user.applyUserCreatedEvent(event); user.append(event); return user; } public static fromEvents(id: string, events: Array): User { const user = new User(id); user.reconstitute(events); return user; } public update(newName: string) { const event = new UserUpdatedEvent(newName); this.applyUserUpdatedEvent(event); this.append(event); } @ApplyEvent(UserCreatedEvent) private applyUserCreatedEvent(event: UserCreatedEvent) { this.name = event.name; this.email = event.email; } @ApplyEvent(UserUpdatedEvent) private applyUserUpdatedEvent(event: UserUpdatedEvent) { this.name = event.newName; } } ``` -------------------------------- ### Build Specific Project with Nx Source: https://github.com/nicktsitlakidis/event-nest/blob/main/AGENTS.md Builds a specific project within the monorepo. This command compiles the project's code. ```bash pnpm nx build core ``` -------------------------------- ### Send Welcome Email on User Creation Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/subscriptions.md This subscription triggers a welcome email to be sent when a UserCreatedEvent occurs. It requires an EmailService to handle email dispatch. ```typescript import { Injectable } from "@nestjs/common"; import { DomainEventSubscription, OnDomainEvent, PublishedDomainEvent } from "@nestjs/event-nest"; import { UserCreatedEvent } from "./user.events"; import { EmailService } from "./email.service"; @Injectable() @DomainEventSubscription(UserCreatedEvent) export class UserWelcomeEmailSubscription implements OnDomainEvent { constructor(private emailService: EmailService) {} async onDomainEvent(event: PublishedDomainEvent): Promise { await this.emailService.sendWelcomeEmail( event.payload.email, event.payload.name ); } } ``` -------------------------------- ### Register PostgreSQL Module Scoped Asynchronously Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Use `registerAsync` for scoped registration with asynchronous configuration. Providers are limited to the importing module. ```typescript static registerAsync(options: PostgreSQLModuleAsyncOptions): DynamicModule ``` -------------------------------- ### Configure ForCountSnapshotStrategy Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Creates a snapshot when the aggregate root version crosses a specified threshold. Use this when a fixed number of version increments is a good indicator for snapshotting. ```typescript import { ForCountSnapshotStrategy } from "@event-nest/core"; new ForCountSnapshotStrategy({ count: 10 }) ``` -------------------------------- ### Run Tests for Specific Library Source: https://github.com/nicktsitlakidis/event-nest/blob/main/CONTRIBUTING.md Execute tests for a particular library within the monorepo. Replace 'core' with the desired library name (e.g., 'mongodb', 'postgresql'). ```bash nx test core ``` ```bash nx test mongodb ``` ```bash nx test postgresql ``` -------------------------------- ### Minimal MongoDB Module Configuration Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Provides the most basic configuration for the EventNest MongoDB module, without enabling snapshots. ```typescript EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/event-store", aggregatesCollection: "aggregates", eventsCollection: "events" }) ``` -------------------------------- ### Create event_nest Schema and Tables Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Use these SQL statements to manually create the event_nest schema and its associated tables (aggregates, events, and optional snapshots) in your PostgreSQL database. Ensure your PostgreSQL user has CREATE TABLE permissions. ```sql CREATE SCHEMA IF NOT EXISTS event_nest; CREATE TABLE event_nest.aggregates ( id UUID PRIMARY KEY NOT NULL, version INTEGER NOT NULL ); CREATE TABLE event_nest.events ( id UUID PRIMARY KEY NOT NULL, aggregate_root_id UUID NOT NULL REFERENCES event_nest.aggregates(id), aggregate_root_version INTEGER NOT NULL, aggregate_root_name TEXT NOT NULL, event_name TEXT NOT NULL, payload JSONB, created_at TIMESTAMP WITH TIME ZONE NOT NULL ); CREATE INDEX idx_events_aggregate_root_id ON event_nest.events(aggregate_root_id); CREATE INDEX idx_events_aggregate_root_name ON event_nest.events(aggregate_root_name); -- Optional: for snapshots CREATE TABLE event_nest.snapshots ( id UUID PRIMARY KEY NOT NULL, aggregate_root_id UUID NOT NULL REFERENCES event_nest.aggregates(id), aggregate_root_version INTEGER NOT NULL, payload JSONB NOT NULL, revision INTEGER NOT NULL ); CREATE INDEX idx_snapshots_aggregate_root_id ON event_nest.snapshots(aggregate_root_id); ``` -------------------------------- ### Enable Concurrent Subscriptions Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Configure the `EventNestMongoDbModule` to enable concurrent execution of subscriptions by setting `concurrentSubscriptions` to `true`. ```typescript @Module({ imports: [ EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/example", aggregatesCollection: "aggregates-collection", eventsCollection: "events-collection", concurrentSubscriptions:true }) ] }) export class AppModule {} ``` -------------------------------- ### Define Aggregate Root (With Snapshots) Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/quick-reference.md Extend SnapshotAware and implement toSnapshot and applySnapshot methods for snapshotting. Configure snapshotRevision in @AggregateRootConfig. ```typescript import { SnapshotAware } from "@event-nest/core"; interface UserSnapshot { name: string; email: string; } @AggregateRootConfig({ name: "User", snapshotRevision: 1 }) export class User extends AggregateRoot implements SnapshotAware { private name: string; private email: string; // ... other methods ... static fromEvents(id: string, events: StoredEvent[], snapshot?: UserSnapshot): User { const user = new User(id); user.reconstitute(events, snapshot); return user; } toSnapshot(): UserSnapshot { return { name: this.name, email: this.email }; } applySnapshot(snapshot: UserSnapshot): void { this.name = snapshot.name; this.email = snapshot.email; } } ``` -------------------------------- ### Injecting and Using EventStore in a NestJS Service Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Demonstrates how to inject the EVENT_STORE provider into a NestJS service and use its methods to create and retrieve aggregate roots. ```typescript import { Injectable, Inject } from "@nestjs/common"; import { EVENT_STORE, EventStore } from "@event-nest/core"; @Injectable() export class UserService { constructor( @Inject(EVENT_STORE) private eventStore: EventStore ) {} async createUser(name: string, email: string): Promise { const userId = await this.eventStore.generateEntityId(); const user = User.createNew(userId, name, email); const userWithPublisher = this.eventStore.addPublisher(user); await userWithPublisher.commit(); return userId; } async getUser(id: string): Promise { const { events, snapshot } = await this.eventStore.findWithSnapshot(User, id); return User.fromEvents(id, events, snapshot); } } ``` -------------------------------- ### Register MongoDB Module Globally (Synchronous) Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Use `forRoot` to register the MongoDB module globally with synchronous configuration. This makes its providers available application-wide. ```typescript import { Module } from "@nestjs/common"; import { EventNestMongoDbModule } from "@event-nest/mongodb"; import { ForCountSnapshotStrategy } from "@event-nest/core"; @Module({ imports: [ EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/my-app", aggregatesCollection: "aggregates", eventsCollection: "events", snapshotCollection: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) }) ] }) export class AppModule {} ``` -------------------------------- ### Minimal MongoDB Configuration Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/INDEX.md Provides the minimal configuration for the Event Nest MongoDB module. Ensure connection URI and collection names are set. ```typescript EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/db", aggregatesCollection: "aggregates", eventsCollection: "events" }) ``` -------------------------------- ### Configure Event Nest with MongoDB and Snapshots Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Configure EventNestMongoDbModule with snapshotting enabled. Provide snapshot collection name and strategy. ```typescript import { ForCountSnapshotStrategy } from "@event-nest/core"; import { EventNestMongoDbModule } from "@event-nest/mongodb"; import { Module } from "@nestjs/common"; @Module({ imports: [ EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/example", aggregatesCollection: "aggregates-collection", eventsCollection: "events-collection", snapshotCollection: "snapshots-collection", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) }), ], }) export class AppModule {} ``` -------------------------------- ### EventNestPostgreSQLModule.register Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/postgresql-module.md Registers the PostgreSQL module scoped to the importing module only. Providers are not available application-wide. ```APIDOC ## EventNestPostgreSQLModule.register ### Description Registers the PostgreSQL module scoped to the importing module only. Providers are not available application-wide. ### Method `static register(options: PostgreSQLModuleOptions): DynamicModule` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **options** (PostgreSQLModuleOptions) - Required - Module configuration ### Request Example None provided in source. ### Response #### Success Response (200) `DynamicModule` #### Response Example None provided in source. ``` -------------------------------- ### Test Specific Project with Nx Source: https://github.com/nicktsitlakidis/event-nest/blob/main/AGENTS.md Executes tests for a specific project within the monorepo. This is preferred for faster feedback during iteration. ```bash pnpm nx test core ``` -------------------------------- ### User Aggregate Root with Snapshotting Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Defines a User aggregate root that implements SnapshotAware, enabling it to be reconstituted from events and snapshots. It includes methods for converting to and applying snapshots. ```typescript @AggregateRootConfig({ name: "User", snapshotRevision: 1 }) export class User extends AggregateRoot implements SnapshotAware { private name: string; private email: string; // ... fields and methods ... static fromEvents(id: string, events: StoredEvent[], snapshot?: UserSnapshot): User { const user = new User(id); user.reconstitute(events, snapshot); return user; } toSnapshot(): UserSnapshot { return { name: this.name, email: this.email }; } applySnapshot(snapshot: UserSnapshot): void { this.name = snapshot.name; this.email = snapshot.email; } } ``` -------------------------------- ### Configure AllOfSnapshotStrategy Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md A composite strategy that requires all provided strategies to agree before creating a snapshot. Acts as a logical AND for complex snapshotting rules. ```typescript import { AllOfSnapshotStrategy, ForAggregateRootsStrategy, ForCountSnapshotStrategy } from "@event-nest/core"; new AllOfSnapshotStrategy([ new ForAggregateRootsStrategy({ aggregates: [User] }), new ForCountSnapshotStrategy({ count: 10 }) ]) ``` -------------------------------- ### Configure Event Nest with PostgreSQL Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Import and configure the EventNestPostgreSQLModule in your NestJS application. Specify table names and schema. ```typescript import { EventNestPostgreSQLModule } from "@event-nest/postgresql"; import { Module } from "@nestjs/common"; @Module({ imports: [ EventNestPostgreSQLModule.forRoot({ aggregatesTableName: "aggregates", connectionUri: "postgresql://postgres:password@localhost:5432/event_nest", eventsTableName: "events", schemaName: "event_nest_schema", ensureTablesExist: true }) ] }) export class AppModule {} ``` -------------------------------- ### Handling SnapshotRevisionMismatchException Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/exceptions.md Demonstrates how to catch and handle a SnapshotRevisionMismatchException by falling back to loading all events. ```typescript try { const { events, snapshot } = await eventStore.findWithSnapshot(User, userId); user.reconstitute(events, snapshot); } catch (error) { if (error instanceof SnapshotRevisionMismatchException) { // The snapshot is incompatible; try loading without it const events = await eventStore.findByAggregateRootId(User, userId); user.reconstitute(events); // Snapshot is ignored, all events replayed } } ``` -------------------------------- ### ConnectionPoolOptions Type Definition Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/types.md Configuration options for the PostgreSQL connection pool, passed to Knex.js. Includes settings for timeouts, idle connections, and pool size. ```typescript interface ConnectionPoolOptions { acquireTimeoutMillis?: number; afterCreate?: Function; createRetryIntervalMillis?: number; createTimeoutMillis?: number; destroyTimeoutMillis?: number; idleTimeoutMillis?: number; log?: (message: string, logLevel: string) => void; max?: number; min?: number; name?: string; priorityRange?: number; propagateCreateError?: boolean; reapIntervalMillis?: number; refreshIdle?: boolean; returnToHead?: boolean; } ``` -------------------------------- ### Register MongoDB Module Globally (Asynchronous) Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Use `forRootAsync` for global registration with asynchronous configuration, suitable when settings depend on other services or environment variables. The configuration object requires `inject` and `useFactory` properties. ```typescript @Module({ imports: [ EventNestMongoDbModule.forRootAsync({ inject: [ConfigService], useFactory: async (configService: ConfigService): Promise => { const host = await configService.get("MONGODB_HOST"); const port = await configService.get("MONGODB_PORT"); return { connectionUri: `mongodb://${host}:${port}/my-app`, aggregatesCollection: "aggregates", eventsCollection: "events" }; } }) ] }) export class AppModule {} ``` -------------------------------- ### Reconstitute Aggregate Root from Events Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/stored-event.md Demonstrates rebuilding an aggregate root from a list of stored events. The process involves deserializing each event's payload and applying it. ```typescript const events = await eventStore.findByAggregateRootId(User, userId); const user = User.fromEvents(userId, events); ``` -------------------------------- ### MongoDB Module Configuration with MongoClient Options Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Configures the MongoDB module with custom MongoClient options for connection pooling and timeouts. ```typescript EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/event-store", aggregatesCollection: "aggregates", eventsCollection: "events", mongoClientConfiguration: { maxPoolSize: 10, minPoolSize: 2, retryWrites: true, serverSelectionTimeoutMS: 5000 } }) ``` -------------------------------- ### Configure AnyOfSnapshotStrategy Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md A composite strategy that creates a snapshot if any of the provided strategies agree. Acts as a logical OR, offering flexibility in snapshotting triggers. ```typescript import { AnyOfSnapshotStrategy, ForCountSnapshotStrategy, ForEventsSnapshotStrategy } from "@event-nest/core"; new AnyOfSnapshotStrategy([ new ForCountSnapshotStrategy({ count: 10 }), new ForEventsSnapshotStrategy({ eventClasses: [UserCreatedEvent] }) ]) ``` -------------------------------- ### Catch UnknownEventException Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/exceptions.md Demonstrates how to catch and handle UnknownEventException. Use this when reconstituting an aggregate root and an event cannot be processed due to missing definitions or handlers. ```typescript try { const events = await eventStore.findByAggregateRootId(User, userId); const user = User.fromEvents(userId, events); } catch (error) { if (error instanceof UnknownEventException) { console.error("Event definition mismatch:", error.message); // Problem: event class was renamed or \"@ApplyEvent\" handler was removed } } ``` -------------------------------- ### toSnapshot Method Implementation Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/snapshots.md Implement this method to return a plain object representing the aggregate's current state. This is called by the event store when a snapshot is to be created. The return type can be synchronous or asynchronous. ```typescript interface UserSnapshot { name: string; email: string; role: string; } toSnapshot(): UserSnapshot { return { name: this.name, email: this.email, role: this.role }; } ``` -------------------------------- ### Asynchronous Subscription Configuration (Variadic Form) Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/subscriptions.md Configures a subscription to process events asynchronously using the variadic form of the DomainEventSubscription decorator. Subscriptions run in the background after commit(). ```typescript @Injectable() @DomainEventSubscription(UserCreatedEvent, UserUpdatedEvent) export class UserEventSubscription implements OnDomainEvent { async onDomainEvent(event: PublishedDomainEvent): Promise { // ... } } ``` -------------------------------- ### PostgreSQLModuleOptions Type Definition Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/types.md Complete configuration for the PostgreSQL event store. Requires table names, connection URI, and schema name, with options for connection pooling and table creation. ```typescript type PostgreSQLModuleOptions = CoreModuleOptions & (SnapshotsDisabled | SnapshotsEnabled) & { aggregatesTableName: string; connectionUri: string; eventsTableName: string; schemaName: string; connectionPool?: ConnectionPoolOptions; ensureTablesExist?: boolean; ssl?: SslOptions; } ``` -------------------------------- ### Configure ForAggregateRootsStrategy Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Creates snapshots only for specified aggregate root classes. Ideal when only a subset of aggregates benefits significantly from snapshotting due to event volume. ```typescript import { ForAggregateRootsStrategy } from "@event-nest/core"; new ForAggregateRootsStrategy({ aggregates: [User, Order] }) ``` -------------------------------- ### Configure ForEventsSnapshotStrategy Source: https://github.com/nicktsitlakidis/event-nest/blob/main/README.md Creates a snapshot when specific event types are committed. Useful for capturing significant state changes represented by particular events. ```typescript import { ForEventsSnapshotStrategy } from "@event-nest/core"; new ForEventsSnapshotStrategy({ eventClasses: [UserCreatedEvent, UserUpdatedEvent] }) ``` -------------------------------- ### EventNestMongoDbModule.forRoot Source: https://github.com/nicktsitlakidis/event-nest/blob/main/_autodocs/mongodb-module.md Registers the MongoDB module globally. Exported providers are available application-wide without additional imports. It takes a MongodbModuleOptions object for configuration. ```APIDOC ## EventNestMongoDbModule.forRoot ### Description Registers the MongoDB module globally. Exported providers are available application-wide without additional imports. ### Method `static forRoot` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **options** (MongodbModuleOptions) - Required - Module configuration ### Request Example ```typescript EventNestMongoDbModule.forRoot({ connectionUri: "mongodb://localhost:27017/my-app", aggregatesCollection: "aggregates", eventsCollection: "events", snapshotCollection: "snapshots", snapshotStrategy: new ForCountSnapshotStrategy({ count: 10 }) }) ``` ### Response #### Success Response (200) `DynamicModule` #### Response Example None explicitly provided, returns `DynamicModule`. ```