first commit

This commit is contained in:
2026-10-03 13:40:26 +02:00
parent 732a5b5a16
commit 566f415fa7
555 changed files with 62704 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
export * from './lib/ports/event-store';
export * from './lib/aggregate-rehydrator';
export * from './lib/shared.module';

View File

@@ -0,0 +1,32 @@
import { Injectable, Type } from '@nestjs/common';
import { EventPublisher } from '@nestjs/cqrs';
import {
NotFoundError,
VersionedAggregateRoot,
} from '@task-stream/api/shared/domain';
import { EventStore } from './ports/event-store';
@Injectable()
export class AggregateRehydrator {
constructor(
private readonly eventStore: EventStore,
private readonly eventPublisher: EventPublisher,
) {}
async rehydrate<T extends VersionedAggregateRoot>(
aggregateId: string,
AggregateCls: Type<T>,
): Promise<T> {
const events = await this.eventStore.getEventsByStreamId(aggregateId);
if (events.length === 0) {
throw new NotFoundError(AggregateCls.name, aggregateId);
}
const AggregateClsWithDispatcher =
this.eventPublisher.mergeClassContext(AggregateCls);
const aggregate = new AggregateClsWithDispatcher(aggregateId);
aggregate.loadFromHistory(events);
return aggregate;
}
}

View File

@@ -0,0 +1,10 @@
import { SerializableEvent } from '@task-stream/api/shared/domain';
/** Append-only store of domain events (the write model). */
export abstract class EventStore {
/** Fails with ConcurrencyError if another process appended to the stream first. */
abstract persist(
eventOrEvents: SerializableEvent | SerializableEvent[],
): Promise<void>;
abstract getEventsByStreamId(streamId: string): Promise<SerializableEvent[]>;
}

View File

@@ -0,0 +1,23 @@
import { DynamicModule, Global, Module, Type } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { AggregateRehydrator } from './aggregate-rehydrator';
/**
* Shared building blocks for every bounded context: the event store (from
* the infrastructure module plugged in by the composition root) and the
* AggregateRehydrator.
*/
@Global()
@Module({})
export class SharedModule {
static withInfrastructure(
infrastructureModule: Type | DynamicModule,
): DynamicModule {
return {
module: SharedModule,
imports: [CqrsModule, infrastructureModule],
providers: [AggregateRehydrator],
exports: [infrastructureModule, AggregateRehydrator],
};
}
}