evtstore
Version:
Event Sourcing with Node.JS
119 lines (98 loc) • 3.13 kB
text/typescript
import { createPersistedAggregate, createProvidedAggregate } from './create-aggregate'
import { EventHandler } from './event-handler'
import {
AggregateStore,
ProvidedAggregate,
Provider,
StorableAggregate,
Event,
HandlerBookmark,
EventMeta,
DomainHandlerOpts,
} from './types'
type StoreOpts = {
provider: Provider<any> | Promise<Provider<any>>
useCache?: boolean
}
type StreamEvents<T extends AggregateStore> = { [K in ExtStreams<T>]: StreamAgg<T, K> }
type ExtStoreAgg<T extends StorableAggregate, U extends string> = T extends StorableAggregate<
any,
any,
U
>
? T
: never
type StreamAgg<T extends AggregateStore, U extends ExtStreams<T>> = ExtStoreAggEvent<
ExtStoreAgg<T[keyof T], U>
>
type ToStoreAgg<T> = T extends StorableAggregate<infer E, infer A> ? ProvidedAggregate<E, A> : never
type Store<T extends AggregateStore> = { [key in keyof T]: ToStoreAgg<T[key]> } & {}
type ExtStoreAggEvent<T> = T extends StorableAggregate<infer E, any, any> ? E : never
type ExtStreams<T extends AggregateStore> = T[keyof T]['stream']
export function createDomain<Tree extends AggregateStore>(opts: StoreOpts, aggregates: Tree) {
return createDomainV2(opts, aggregates)
}
export function createDomainV2<Tree extends AggregateStore>(opts: StoreOpts, aggregates: Tree) {
type EventTree = StreamEvents<Tree>
const _store: any = {}
for (const [key, storableAgg] of Object.entries(aggregates)) {
const aggOpts = {
...storableAgg,
provider: opts.provider,
useCache: opts.useCache,
}
_store[key] =
aggOpts.persistAggregate && aggOpts.version
? createPersistedAggregate(aggOpts)
: createProvidedAggregate(aggOpts)
}
const createHandler = <S extends ExtStreams<Tree>[]>(
bookmark: HandlerBookmark,
streams: S,
options: DomainHandlerOpts = {}
) => {
type Evt = EventTree[S[number]]
type CB = (id: string, event: Event, meta: EventMeta) => any
const callbacks = new Map<string, CB>()
const handler = new EventHandler<Evt>({
bookmark,
provider: opts.provider,
stream: streams,
...options,
})
const handlerCallback = (id: string, event: Event, meta: EventMeta) => {
const cb = callbacks.get(`${meta.stream}-${event.type}`)
if (!cb) return
return cb(id, event as any, meta)
}
const handle = <Stream extends S[number], Type extends EventTree[Stream]['type']>(
stream: Stream,
event: Type,
callback: (
id: string,
event: Extract<EventTree[Stream], { type: Type }>,
meta: EventMeta
) => any
) => {
callbacks.set(`${stream}-${event}`, callback as any)
handler.handle(event, handlerCallback)
}
return {
handle,
name: handler.name,
start: handler.start,
stop: handler.stop,
runOnce: handler.runOnce,
run: handler.run,
setPosition: handler.setPosition,
getPosition: handler.getPosition,
reset: handler.reset,
__handler: handler,
}
}
const domain = _store as Store<Tree>
return {
domain,
createHandler,
}
}