evtstore
Version:
Event Sourcing with Node.JS
181 lines (152 loc) • 4.92 kB
text/typescript
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
}