Refactor event interface to use VariantTag for event types
This commit is contained in:
parent
4ea00f515b
commit
38e23ba095
@ -7,7 +7,7 @@ export interface TaggedVariant<TTag extends string> {
|
|||||||
|
|
||||||
export type VariantFromTag<
|
export type VariantFromTag<
|
||||||
TVariant extends TaggedVariant<string>,
|
TVariant extends TaggedVariant<string>,
|
||||||
TTag extends TVariant[typeof VariantTag],
|
TTag extends TVariant[VariantTag],
|
||||||
> = Extract<TVariant, { [VariantTag]: TTag }>;
|
> = Extract<TVariant, { [VariantTag]: TTag }>;
|
||||||
|
|
||||||
export namespace Variant {
|
export namespace Variant {
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
import { AsyncResult, MaybePromise, PosixDate } from "@fabric/core";
|
import {
|
||||||
|
AsyncResult,
|
||||||
|
MaybePromise,
|
||||||
|
PosixDate,
|
||||||
|
VariantFromTag,
|
||||||
|
VariantTag,
|
||||||
|
} from "@fabric/core";
|
||||||
import { StoreQueryError } from "../errors/query-error.js";
|
import { StoreQueryError } from "../errors/query-error.js";
|
||||||
import { UUID } from "../types/uuid.js";
|
import { UUID } from "../types/uuid.js";
|
||||||
import { Event, EventFromKey } from "./event.js";
|
import { Event } from "./event.js";
|
||||||
import { StoredEvent } from "./stored-event.js";
|
import { StoredEvent } from "./stored-event.js";
|
||||||
|
|
||||||
export interface EventStore<TEvents extends Event> {
|
export interface EventStore<TEvents extends Event> {
|
||||||
@ -16,9 +22,9 @@ export interface EventStore<TEvents extends Event> {
|
|||||||
streamId: UUID,
|
streamId: UUID,
|
||||||
): AsyncResult<StoredEvent<TEvents>[], StoreQueryError>;
|
): AsyncResult<StoredEvent<TEvents>[], StoreQueryError>;
|
||||||
|
|
||||||
subscribe<TEventKey extends TEvents["type"]>(
|
subscribe<TEventKey extends TEvents[VariantTag]>(
|
||||||
events: TEventKey[],
|
events: TEventKey[],
|
||||||
subscriber: EventSubscriber<EventFromKey<TEvents, TEventKey>>,
|
subscriber: EventSubscriber<VariantFromTag<TEvents, TEventKey>>,
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { VariantTag } from "@fabric/core";
|
||||||
import { UUID } from "../types/uuid.js";
|
import { UUID } from "../types/uuid.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event is a tagged variant with a payload and a timestamp.
|
* An event is a tagged variant with a payload and a timestamp.
|
||||||
*/
|
*/
|
||||||
export interface Event<TTag extends string = string, TPayload = any> {
|
export interface Event<TTag extends string = string, TPayload = any> {
|
||||||
readonly type: TTag;
|
readonly [VariantTag]: TTag;
|
||||||
readonly id: UUID;
|
readonly id: UUID;
|
||||||
readonly streamId: UUID;
|
readonly streamId: UUID;
|
||||||
readonly payload: TPayload;
|
readonly payload: TPayload;
|
||||||
@ -13,5 +14,5 @@ export interface Event<TTag extends string = string, TPayload = any> {
|
|||||||
|
|
||||||
export type EventFromKey<
|
export type EventFromKey<
|
||||||
TEvents extends Event,
|
TEvents extends Event,
|
||||||
TKey extends TEvents["type"],
|
TKey extends TEvents[VariantTag],
|
||||||
> = Extract<TEvents, { type: TKey }>;
|
> = Extract<TEvents, { [VariantTag]: TKey }>;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ describe("Event Store", () => {
|
|||||||
const newUUID = UUIDGeneratorMock.generate();
|
const newUUID = UUIDGeneratorMock.generate();
|
||||||
|
|
||||||
const userCreated: UserCreated = {
|
const userCreated: UserCreated = {
|
||||||
type: "UserCreated",
|
_tag: "UserCreated",
|
||||||
id: newUUID,
|
id: newUUID,
|
||||||
streamId: newUUID,
|
streamId: newUUID,
|
||||||
payload: { name: "test" },
|
payload: { name: "test" },
|
||||||
@ -41,7 +41,7 @@ describe("Event Store", () => {
|
|||||||
expect(events[0]).toEqual({
|
expect(events[0]).toEqual({
|
||||||
id: newUUID,
|
id: newUUID,
|
||||||
streamId: newUUID,
|
streamId: newUUID,
|
||||||
type: "UserCreated",
|
_tag: "UserCreated",
|
||||||
version: BigInt(1),
|
version: BigInt(1),
|
||||||
timestamp: expect.any(PosixDate),
|
timestamp: expect.any(PosixDate),
|
||||||
payload: { name: "test" },
|
payload: { name: "test" },
|
||||||
@ -52,7 +52,7 @@ describe("Event Store", () => {
|
|||||||
const newUUID = UUIDGeneratorMock.generate();
|
const newUUID = UUIDGeneratorMock.generate();
|
||||||
|
|
||||||
const userCreated: UserCreated = {
|
const userCreated: UserCreated = {
|
||||||
type: "UserCreated",
|
_tag: "UserCreated",
|
||||||
id: newUUID,
|
id: newUUID,
|
||||||
streamId: newUUID,
|
streamId: newUUID,
|
||||||
payload: { name: "test" },
|
payload: { name: "test" },
|
||||||
@ -68,7 +68,7 @@ describe("Event Store", () => {
|
|||||||
expect(subscriber).toHaveBeenCalledWith({
|
expect(subscriber).toHaveBeenCalledWith({
|
||||||
id: newUUID,
|
id: newUUID,
|
||||||
streamId: newUUID,
|
streamId: newUUID,
|
||||||
type: "UserCreated",
|
_tag: "UserCreated",
|
||||||
version: BigInt(1),
|
version: BigInt(1),
|
||||||
timestamp: expect.any(PosixDate),
|
timestamp: expect.any(PosixDate),
|
||||||
payload: { name: "test" },
|
payload: { name: "test" },
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { AsyncResult, MaybePromise, PosixDate, Run } from "@fabric/core";
|
import {
|
||||||
|
AsyncResult,
|
||||||
|
MaybePromise,
|
||||||
|
PosixDate,
|
||||||
|
Run,
|
||||||
|
VariantTag,
|
||||||
|
} from "@fabric/core";
|
||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
EventFromKey,
|
EventFromKey,
|
||||||
@ -19,7 +25,7 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
private streamVersions = new Map<UUID, bigint>();
|
private streamVersions = new Map<UUID, bigint>();
|
||||||
|
|
||||||
private eventSubscribers = new Map<
|
private eventSubscribers = new Map<
|
||||||
TEvents["type"],
|
TEvents[VariantTag],
|
||||||
EventSubscriber<TEvents>[]
|
EventSubscriber<TEvents>[]
|
||||||
>();
|
>();
|
||||||
|
|
||||||
@ -34,7 +40,7 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
await this.db.run(
|
await this.db.run(
|
||||||
`CREATE TABLE IF NOT EXISTS events (
|
`CREATE TABLE IF NOT EXISTS events (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
type TEXT NOT NULL,
|
_tag TEXT NOT NULL,
|
||||||
streamId TEXT NOT NULL,
|
streamId TEXT NOT NULL,
|
||||||
version INTEGER NOT NULL,
|
version INTEGER NOT NULL,
|
||||||
timestamp NUMERIC NOT NULL,
|
timestamp NUMERIC NOT NULL,
|
||||||
@ -57,13 +63,13 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
{
|
{
|
||||||
$id: streamId,
|
$id: streamId,
|
||||||
},
|
},
|
||||||
(event) => ({
|
(e) => ({
|
||||||
id: event.id,
|
id: e.id,
|
||||||
streamId: event.streamId,
|
streamId: e.streamId,
|
||||||
type: event.type,
|
_tag: e._tag,
|
||||||
version: BigInt(event.version),
|
version: BigInt(e.version),
|
||||||
timestamp: new PosixDate(event.timestamp),
|
timestamp: new PosixDate(e.timestamp),
|
||||||
payload: JSONUtils.parse(event.payload),
|
payload: JSONUtils.parse(e.payload),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return events;
|
return events;
|
||||||
@ -95,7 +101,7 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
event: StoredEvent<TEvents>,
|
event: StoredEvent<TEvents>,
|
||||||
): AsyncResult<void> {
|
): AsyncResult<void> {
|
||||||
return AsyncResult.from(async () => {
|
return AsyncResult.from(async () => {
|
||||||
const subscribers = this.eventSubscribers.get(event.type) || [];
|
const subscribers = this.eventSubscribers.get(event[VariantTag]) || [];
|
||||||
await Promise.all(subscribers.map((subscriber) => subscriber(event)));
|
await Promise.all(subscribers.map((subscriber) => subscriber(event)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -121,13 +127,13 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe<TEventKey extends TEvents["type"]>(
|
subscribe<TEventKey extends TEvents[VariantTag]>(
|
||||||
events: TEventKey[],
|
eventNames: TEventKey[],
|
||||||
subscriber: (
|
subscriber: (
|
||||||
event: StoredEvent<EventFromKey<TEvents, TEventKey>>,
|
event: StoredEvent<EventFromKey<TEvents, TEventKey>>,
|
||||||
) => MaybePromise<void>,
|
) => MaybePromise<void>,
|
||||||
): void {
|
): void {
|
||||||
events.forEach((event) => {
|
eventNames.forEach((event) => {
|
||||||
const subscribers = this.eventSubscribers.get(event) || [];
|
const subscribers = this.eventSubscribers.get(event) || [];
|
||||||
const newSubscribers = [
|
const newSubscribers = [
|
||||||
...subscribers,
|
...subscribers,
|
||||||
@ -157,12 +163,12 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
timestamp: new PosixDate(),
|
timestamp: new PosixDate(),
|
||||||
};
|
};
|
||||||
await this.db.runPrepared(
|
await this.db.runPrepared(
|
||||||
`INSERT INTO events (id, streamId, type, version, timestamp, payload)
|
`INSERT INTO events (id, streamId, _tag, version, timestamp, payload)
|
||||||
VALUES ($id, $streamId, $type, $version, $timestamp, $payload)`,
|
VALUES ($id, $streamId, $_tag, $version, $timestamp, $payload)`,
|
||||||
{
|
{
|
||||||
$id: storedEvent.id,
|
$id: storedEvent.id,
|
||||||
$streamId: streamId,
|
$streamId: streamId,
|
||||||
$type: storedEvent.type,
|
$_tag: storedEvent[VariantTag],
|
||||||
$version: storedEvent.version.toString(),
|
$version: storedEvent.version.toString(),
|
||||||
$timestamp: storedEvent.timestamp.timestamp,
|
$timestamp: storedEvent.timestamp.timestamp,
|
||||||
$payload: JSON.stringify(storedEvent.payload),
|
$payload: JSON.stringify(storedEvent.payload),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user