UNPKG

evtstore

Version:

Event Sourcing with Node.JS

181 lines (152 loc) 4.92 kB
export type HandlerHooks = { preRun?: () => Promise<void> postRun?: (events: number, handled: number) => Promise<void> } export type Event = { type: string } export type Command = { type: string } export type Aggregate = {} export type StoreEvent<E = unknown, A = unknown> = EventMeta & { event: E & { __persisted?: A } } export type ProviderBookmark = { readonly name: string getPosition(): Promise<any> setPosition(position: any): Promise<void> } export type DomainOptions<E extends Event, A extends Aggregate> = { aggregate: () => A stream: string fold: Fold<E, A> provider: Provider<E> | Promise<Provider<E>> useCache?: boolean } export type StreamsHandler<T extends { [key: string]: Event }> = < TStream extends keyof T, TType extends T[TStream]['type'] >( stream: TStream, type: TType, handler: (id: string, event: Ext<T[TStream], TType>, meta: EventMeta) => any ) => void export type HandlerBookmark = string | ProviderBookmark export type EventMeta = { stream: string position: any version: number timestamp: Date aggregateId: string } export type ErrorCallback = ( err: any, stream: string, bookmark: string, event?: Event & { [key: string]: any } ) => any type ID = { aggregateId: string } export type BaseAggregate = { version: number; aggregateId: string; __pv?: string } export type Fold<E extends Event, A extends Aggregate> = ( ev: E, agg: A & BaseAggregate, meta: EventMeta ) => Partial<A> export type Provider<Evt extends Event> = { driver: string onError: ErrorCallback getPosition(bookmark: string): Promise<any> setPosition(bookmark: string, position: any): Promise<void> getEventsFrom( stream: string | string[], position: any, limit?: number ): Promise<Array<StoreEvent<Evt>>> getEventsFor( stream: string, aggregateId: string, fromPosition?: any ): Promise<Array<StoreEvent<Evt>>> getLastEventFor( stream: string | string[], aggregateId?: string ): Promise<StoreEvent<Evt & { __persisted?: any }> | undefined> createEvents( stream: string, aggregateId: string, version: number, event: Evt[] ): Array<StoreEvent<Evt>> append( stream: string, aggregateId: string, version: number, event: StoreEvent<Evt>[] ): Promise<Array<StoreEvent<Evt>>> limit?: number } export type Handler<E extends Event> = { start(): void stop(): void reset(): void runOnce(): Promise<number> handle: <T extends E['type']>( type: T, cb: (aggregateId: string, event: Ext<E, T>, meta: EventMeta) => Promise<any> ) => void handlers: (body: HandlerBody<E>) => void name: string } export type Ext<E extends Event, T extends E['type']> = E extends { type: T } ? E : never export type CommandHandler<E extends Event, A extends Aggregate, C extends Command> = { [key in C['type']]: (cmd: OptCmd<C, key> & ID, agg: A & BaseAggregate) => Promise<E | E[] | void> } type OptCmd<C extends Command, T extends C['type']> = Omit<Ext<C, T>, 'type'> & { type: T } export type DomainHandlerOpts = { hooks?: HandlerHooks /** Start handling events from the end of the stream */ tailStream?: boolean /** Every time the handler starts, always start from the end of the stream */ alwaysTailStream?: boolean /** When a handler throws, continue processing events */ continueOnError?: boolean } export type Domain<E extends Event, A extends Aggregate, C extends Command> = { handler(bookmark: string, options?: DomainHandlerOpts): Handler<E> command: CmdBody<C, A> getAggregate( id: string ): Promise<ExecutableAggregate<C, A> & { aggregate: Readonly<A & BaseAggregate> }> retry?: boolean } export type CmdBody<C extends Command, A extends Aggregate> = { [cmd in C['type']]: (aggId: string, body: ExtCmd<C, cmd>) => Promise<A & BaseAggregate> } export type ExecutableAggregate<C extends Command, A extends Aggregate> = { [cmd in C['type']]: ( body: ExtCmd<C, cmd> ) => Promise<ExecutableAggregate<C, A> & { aggregate: Readonly<A & BaseAggregate> }> } type ExtCmd<C extends Command, T extends C['type']> = Omit<Ext<C, T>, 'type'> export type HandlerBody<E extends Event> = { [evt in E['type']]?: (id: string, evt: Ext<E, evt>, meta: EventMeta) => Promise<any> } export type StorableAggregate< E extends Event = any, A extends Aggregate = any, S extends string = string > = { stream: S fold: Fold<E, A> aggregate: () => A version?: string persistAggregate?: boolean } export type AggregateStore = { [key: string]: StorableAggregate } export type ProvidedAggregate<E extends Event, A extends Aggregate, S extends string = string> = { stream: S provider: Provider<E> | Promise<Provider<E>> getAggregate: (id: string) => Promise<A & BaseAggregate> toNextAggregate: (prev: A & BaseAggregate, event: StoreEvent<E>) => A & BaseAggregate version?: string persistAggregate?: boolean }