effect-cqrs 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +326 -2
  3. package/dist/Aggregate.d.ts +127 -0
  4. package/dist/Aggregate.d.ts.map +1 -0
  5. package/dist/Aggregate.js +59 -0
  6. package/dist/Aggregate.js.map +1 -0
  7. package/dist/AggregateService.d.ts +154 -0
  8. package/dist/AggregateService.d.ts.map +1 -0
  9. package/dist/AggregateService.js +407 -0
  10. package/dist/AggregateService.js.map +1 -0
  11. package/dist/Causation.d.ts +34 -0
  12. package/dist/Causation.d.ts.map +1 -0
  13. package/dist/Causation.js +32 -0
  14. package/dist/Causation.js.map +1 -0
  15. package/dist/Checkpoints.d.ts +19 -0
  16. package/dist/Checkpoints.d.ts.map +1 -0
  17. package/dist/Checkpoints.js +18 -0
  18. package/dist/Checkpoints.js.map +1 -0
  19. package/dist/EventStore.d.ts +132 -0
  20. package/dist/EventStore.d.ts.map +1 -0
  21. package/dist/EventStore.js +156 -0
  22. package/dist/EventStore.js.map +1 -0
  23. package/dist/EventVersions.d.ts +64 -0
  24. package/dist/EventVersions.d.ts.map +1 -0
  25. package/dist/EventVersions.js +55 -0
  26. package/dist/EventVersions.js.map +1 -0
  27. package/dist/Fixture.d.ts +106 -0
  28. package/dist/Fixture.d.ts.map +1 -0
  29. package/dist/Fixture.js +149 -0
  30. package/dist/Fixture.js.map +1 -0
  31. package/dist/Metadata.d.ts +42 -0
  32. package/dist/Metadata.d.ts.map +1 -0
  33. package/dist/Metadata.js +39 -0
  34. package/dist/Metadata.js.map +1 -0
  35. package/dist/PostgresEventStore.d.ts +30 -0
  36. package/dist/PostgresEventStore.d.ts.map +1 -0
  37. package/dist/PostgresEventStore.js +153 -0
  38. package/dist/PostgresEventStore.js.map +1 -0
  39. package/dist/Saga.d.ts +105 -0
  40. package/dist/Saga.d.ts.map +1 -0
  41. package/dist/Saga.js +97 -0
  42. package/dist/Saga.js.map +1 -0
  43. package/dist/SqliteEventStore.d.ts +25 -0
  44. package/dist/SqliteEventStore.d.ts.map +1 -0
  45. package/dist/SqliteEventStore.js +142 -0
  46. package/dist/SqliteEventStore.js.map +1 -0
  47. package/dist/ViewModel.d.ts +90 -0
  48. package/dist/ViewModel.d.ts.map +1 -0
  49. package/dist/ViewModel.js +172 -0
  50. package/dist/ViewModel.js.map +1 -0
  51. package/dist/index.d.ts +13 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +13 -0
  54. package/dist/index.js.map +1 -0
  55. package/package.json +89 -3
  56. package/src/Aggregate.ts +205 -0
  57. package/src/AggregateService.ts +705 -0
  58. package/src/Causation.ts +48 -0
  59. package/src/Checkpoints.ts +23 -0
  60. package/src/EventStore.ts +270 -0
  61. package/src/EventVersions.ts +104 -0
  62. package/src/Fixture.ts +298 -0
  63. package/src/Metadata.ts +52 -0
  64. package/src/PostgresEventStore.ts +199 -0
  65. package/src/Saga.ts +176 -0
  66. package/src/SqliteEventStore.ts +196 -0
  67. package/src/ViewModel.ts +350 -0
  68. package/src/index.ts +12 -0
package/dist/Saga.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Sagas: reactions that span several aggregates and outlive one command, run as
3
+ * durable workflows from `effect/workflow`.
4
+ *
5
+ * A saga has two halves. Its trigger is a view model that follows the log from a
6
+ * checkpoint and starts a workflow for each event it cares about. The workflow is
7
+ * the process itself: each command it runs is an `Activity`, so when the engine
8
+ * replays the workflow after a restart it skips the commands that already
9
+ * finished, and `withCompensation` undoes earlier steps when a later one fails.
10
+ *
11
+ * Triggers see an event at least once, and the workflow's idempotency key turns
12
+ * a second start into a join of the first execution. That only holds while the
13
+ * engine remembers the execution, so pair `WorkflowEngine.layerMemory` with
14
+ * in-memory checkpoints, as for view models, and a durable engine such as
15
+ * `ClusterWorkflowEngine` from `effect/cluster` with durable ones.
16
+ *
17
+ * An activity that is cut off before its result is saved runs again, so the
18
+ * commands a saga runs should be idempotent, for instance by recording the id of
19
+ * the transfer they belong to.
20
+ */
21
+ import { Effect, Layer, Schedule, Schema } from "effect";
22
+ import { Activity, Workflow } from "effect/workflow";
23
+ import * as Causation from "./Causation.js";
24
+ import * as ViewModel from "./ViewModel.js";
25
+ /**
26
+ * The causation a workflow carries in its payload. A workflow body runs in the
27
+ * engine's fibers, not in the trigger's, so this is how commands it runs learn
28
+ * which event caused them.
29
+ */
30
+ export const CausationSchema = Schema.Struct({
31
+ correlationId: Schema.String,
32
+ causationId: Schema.String
33
+ });
34
+ /**
35
+ * Defines the workflow of a saga. It is `Workflow.make` with a `causation` field
36
+ * added to the payload, which {@link start} fills in and {@link toLayer} applies.
37
+ */
38
+ export const workflow = (tag, options) => Workflow.make(tag, {
39
+ ...options,
40
+ payload: { ...options.payload, causation: CausationSchema }
41
+ });
42
+ /**
43
+ * Registers the body of a saga's workflow with the engine. Commands the body
44
+ * runs record the event that started the saga as their cause.
45
+ */
46
+ export const toLayer = (self, execute) => self.toLayer((payload, executionId) => Causation.withCausation(execute(payload, executionId), payload.causation));
47
+ /**
48
+ * Starts a saga's workflow without waiting for it, and returns its execution id.
49
+ * Inside a trigger the event being handled becomes the saga's cause; elsewhere
50
+ * the current causation does, or a new correlation when there is none.
51
+ */
52
+ export const start = (self, payload) => Causation.CurrentCausation.use((current) => {
53
+ const id = crypto.randomUUID();
54
+ const causation = current ?? { correlationId: id, causationId: id };
55
+ return self.execute({ ...payload, causation }, { discard: true });
56
+ });
57
+ /**
58
+ * The trigger of a saga: a view model whose handlers start workflows with
59
+ * {@link start}, so it keeps a checkpoint and retries like view models do. Run it
60
+ * with {@link layer}, or with `ViewModel.layer` next to the other view models,
61
+ * where replaying it starts every saga again and joins the executions the engine
62
+ * still remembers.
63
+ */
64
+ export const make = (options) => ViewModel.make({ ...options, dispose: Effect.void });
65
+ /**
66
+ * Runs triggers in the background for as long as the layer lives, apart from
67
+ * the view models. Workflows usually run commands, so they sit on top of the
68
+ * command side, which view models are often provided to.
69
+ */
70
+ export const layer = (sagas) => Layer.provide(Layer.empty, ViewModel.layer(sagas));
71
+ /** What a saga keeps of a command it ran: enough to wait for view models to see it. */
72
+ export const CommandOutcome = Schema.Struct({
73
+ aggregateId: Schema.String,
74
+ version: Schema.Int,
75
+ position: Schema.Int
76
+ });
77
+ // Losing a race or a store error usually passes, so try again before giving up.
78
+ const transient = (error) => Schema.is(Schema.Struct({ _tag: Schema.Literals(["ConcurrencyConflict", "EventStoreError"]) }))(error);
79
+ const transientRetry = Schedule.exponential("50 millis").pipe(Schedule.jittered);
80
+ /**
81
+ * Runs a command as an activity of a saga's workflow, so a replay of the
82
+ * workflow returns the saved outcome instead of running it again.
83
+ *
84
+ * Failures matching `error`, such as a domain error the saga reacts to, are
85
+ * saved like a success. A lost race or a store error is retried a few times.
86
+ * Anything else ends the workflow as a defect, which runs its compensations.
87
+ */
88
+ export const command = (name, options) => {
89
+ const expected = options.error === undefined ? () => false : Schema.is(Schema.toType(options.error));
90
+ return Activity.make({
91
+ name,
92
+ success: CommandOutcome,
93
+ error: options.error,
94
+ execute: options.execute.pipe(Effect.retry({ while: transient, schedule: transientRetry, times: 5 }), Effect.map(({ aggregateId, position, version }) => ({ aggregateId, version, position })), Effect.catch((error) => expected(error) ? Effect.fail(error) : Effect.die(error)))
95
+ });
96
+ };
97
+ //# sourceMappingURL=Saga.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Saga.js","sourceRoot":"","sources":["../src/Saga.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Durable storage on SQLite, through any `effect/sql` SQLite driver.
3
+ *
4
+ * The tables are created on startup. The schema relies on SQLite's single writer
5
+ * for positions to follow commit order, so it is not portable to other databases.
6
+ */
7
+ import { Effect, Layer } from "effect";
8
+ import { SqlClient } from "effect/sql";
9
+ import { Checkpoints } from "./Checkpoints.ts";
10
+ import * as EventStore from "./EventStore.ts";
11
+ export interface Options {
12
+ readonly eventsTable?: string;
13
+ readonly checkpointsTable?: string;
14
+ }
15
+ export declare const make: (options?: Options | undefined) => Effect.Effect<{
16
+ append(input: EventStore.AppendInput): Effect.Effect<ReadonlyArray<EventStore.StoredEvent>, EventStore.ConcurrencyConflict | EventStore.EventStoreError>;
17
+ read(filter?: EventStore.EventFilter): import("effect/Stream").Stream<EventStore.StoredEvent, EventStore.EventStoreError>;
18
+ subscribe(filter?: EventStore.EventFilter): import("effect/Stream").Stream<EventStore.StoredEvent, EventStore.EventStoreError>;
19
+ readonly head: Effect.Effect<number, EventStore.EventStoreError>;
20
+ readonly changes: import("effect/Stream").Stream<number>;
21
+ }, EventStore.EventStoreError, SqlClient.SqlClient>;
22
+ export declare const layer: (options?: Options) => Layer.Layer<EventStore.EventStore, EventStore.EventStoreError, SqlClient.SqlClient>;
23
+ /** Keeps checkpoints in the same database, for view models that are durable too. */
24
+ export declare const layerCheckpoints: (options?: Options) => Layer.Layer<Checkpoints, EventStore.EventStoreError, SqlClient.SqlClient>;
25
+ //# sourceMappingURL=SqliteEventStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqliteEventStore.d.ts","sourceRoot":"","sources":["../src/SqliteEventStore.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAmB,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAiB,MAAM,YAAY,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACnC;AAqBD,eAAO,MAAM,IAAI;;;;;;mDA4Hf,CAAA;AAEF,eAAO,MAAM,KAAK,aAAc,OAAO,KAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,eAAe,EAAE,SAAS,CAAC,SAAS,CACxE,CAAA;AAEpD,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,aAAc,OAAO,KAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,CAAC,eAAe,EAAE,SAAS,CAAC,SAAS,CA8B1H,CAAA"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Durable storage on SQLite, through any `effect/sql` SQLite driver.
3
+ *
4
+ * The tables are created on startup. The schema relies on SQLite's single writer
5
+ * for positions to follow commit order, so it is not portable to other databases.
6
+ */
7
+ import { Clock, DateTime, Effect, Layer, Schema } from "effect";
8
+ import { SqlClient } from "effect/sql";
9
+ import { Checkpoints } from "./Checkpoints.js";
10
+ import * as EventStore from "./EventStore.js";
11
+ const EventRow = Schema.Struct({
12
+ ...EventStore.StoredEvent.fields,
13
+ payload: Schema.fromJsonString(Schema.Json),
14
+ metadata: Schema.fromJsonString(Schema.JsonObject)
15
+ });
16
+ const decodeRows = Schema.decodeUnknownEffect(Schema.Array(EventRow));
17
+ const storeError = (operation) => (cause) => new EventStore.EventStoreError({ operation, cause });
18
+ const isUniqueViolation = (error) => error.reason._tag === "UniqueViolation";
19
+ const addedColumns = [
20
+ ["schema_version", "INTEGER NOT NULL DEFAULT 1"],
21
+ ["correlation_id", "TEXT"],
22
+ ["causation_id", "TEXT"],
23
+ ["metadata", "TEXT NOT NULL DEFAULT '{}'"]
24
+ ];
25
+ export const make = Effect.fnUntraced(function* (options) {
26
+ const sql = yield* SqlClient.SqlClient;
27
+ const events = sql(options?.eventsTable ?? "cqrs_events");
28
+ const indexPrefix = options?.eventsTable ?? "cqrs_events";
29
+ yield* sql `
30
+ CREATE TABLE IF NOT EXISTS ${events} (
31
+ position INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ aggregate_name TEXT NOT NULL,
33
+ aggregate_id TEXT NOT NULL,
34
+ version INTEGER NOT NULL,
35
+ type TEXT NOT NULL,
36
+ schema_version INTEGER NOT NULL DEFAULT 1,
37
+ payload TEXT NOT NULL,
38
+ timestamp INTEGER NOT NULL,
39
+ correlation_id TEXT,
40
+ causation_id TEXT,
41
+ metadata TEXT NOT NULL DEFAULT '{}',
42
+ UNIQUE (aggregate_name, aggregate_id, version)
43
+ )
44
+ `.pipe(Effect.mapError(storeError("initialize")));
45
+ // Tables created before these columns existed get them here. SQLite has no
46
+ // `ADD COLUMN IF NOT EXISTS`, so the existing columns are listed first.
47
+ const existing = yield* sql `PRAGMA table_info(${events})`.pipe(Effect.map((rows) => new Set(rows.map(({ name }) => name))), Effect.mapError(storeError("initialize")));
48
+ yield* Effect.forEach(addedColumns.filter(([name]) => !existing.has(name)), ([name, definition]) => sql `ALTER TABLE ${events} ADD COLUMN ${sql(name)} ${sql.literal(definition)}`.pipe(Effect.mapError(storeError("initialize"))), { discard: true });
49
+ yield* sql `
50
+ CREATE INDEX IF NOT EXISTS ${sql(`${indexPrefix}_type_idx`)} ON ${events} (type, position)
51
+ `.pipe(Effect.mapError(storeError("initialize")));
52
+ const columns = sql.literal(`
53
+ position,
54
+ aggregate_name AS "aggregateName",
55
+ aggregate_id AS "aggregateId",
56
+ version,
57
+ type,
58
+ schema_version AS "schemaVersion",
59
+ payload,
60
+ timestamp,
61
+ correlation_id AS "correlationId",
62
+ causation_id AS "causationId",
63
+ metadata
64
+ `);
65
+ const append = (input) => {
66
+ const conflict = new EventStore.ConcurrencyConflict({
67
+ aggregateName: input.aggregateName,
68
+ aggregateId: input.aggregateId,
69
+ expectedVersion: input.expectedVersion
70
+ });
71
+ return Effect.gen(function* () {
72
+ const [current] = yield* sql `
73
+ SELECT COALESCE(MAX(version), 0) AS version FROM ${events}
74
+ WHERE aggregate_name = ${input.aggregateName} AND aggregate_id = ${input.aggregateId}
75
+ `;
76
+ if (current?.version !== input.expectedVersion) {
77
+ return yield* conflict;
78
+ }
79
+ const timestamp = yield* Clock.currentTimeMillis;
80
+ const metadata = JSON.stringify(input.metadata ?? {});
81
+ return yield* Effect.forEach(input.events, (event, index) => sql `
82
+ INSERT INTO ${events} (
83
+ aggregate_name, aggregate_id, version, type, schema_version, payload, timestamp, correlation_id, causation_id,
84
+ metadata
85
+ )
86
+ VALUES (
87
+ ${input.aggregateName},
88
+ ${input.aggregateId},
89
+ ${input.expectedVersion + index + 1},
90
+ ${event.type},
91
+ ${event.schemaVersion ?? 1},
92
+ ${JSON.stringify(event.payload)},
93
+ ${timestamp},
94
+ ${input.correlationId ?? null},
95
+ ${input.causationId ?? null},
96
+ ${metadata}
97
+ )
98
+ RETURNING position
99
+ `.pipe(Effect.map(([row]) => EventStore.toStored(input, event, index, row.position, DateTime.makeUnsafe(timestamp)))));
100
+ }).pipe(sql.withTransaction,
101
+ // The version check can't see a writer racing it on a driver that opens
102
+ // deferred transactions, but the UNIQUE constraint can.
103
+ Effect.catchTag("SqlError", (error) => Effect.fail(isUniqueViolation(error) ? conflict : storeError("append")(error))));
104
+ };
105
+ const readBatch = (filter, afterPosition, limit) => {
106
+ const oneStream = EventStore.isOneStream(filter);
107
+ const clauses = [
108
+ sql `position > ${afterPosition}`,
109
+ ...(oneStream ? [sql `version > ${filter.afterVersion ?? 0}`] : []),
110
+ ...(filter.aggregateNames === undefined ? [] : [sql.in("aggregate_name", filter.aggregateNames)]),
111
+ ...(filter.aggregateIds === undefined ? [] : [sql.in("aggregate_id", filter.aggregateIds)]),
112
+ ...(filter.eventTypes === undefined ? [] : [sql.in("type", filter.eventTypes)]),
113
+ ...(filter.startTime === undefined ? [] : [sql `timestamp >= ${DateTime.toEpochMillis(filter.startTime)}`]),
114
+ ...(filter.finishTime === undefined ? [] : [sql `timestamp < ${DateTime.toEpochMillis(filter.finishTime)}`])
115
+ ];
116
+ // Inside a stream versions follow positions, and only versions are indexed.
117
+ const order = oneStream ? sql `version` : sql `position`;
118
+ return sql `SELECT ${columns} FROM ${events} WHERE ${sql.and(clauses)} ORDER BY ${order} LIMIT ${limit}`.pipe(Effect.flatMap(decodeRows), Effect.mapError(storeError("read")));
119
+ };
120
+ const head = sql `SELECT COALESCE(MAX(position), 0) AS position FROM ${events}`.pipe(Effect.map(([row]) => row?.position ?? 0), Effect.mapError(storeError("head")));
121
+ return yield* EventStore.make({ append, readBatch, head });
122
+ });
123
+ export const layer = (options) => Layer.effect(EventStore.EventStore, make(options));
124
+ /** Keeps checkpoints in the same database, for view models that are durable too. */
125
+ export const layerCheckpoints = (options) => Layer.effect(Checkpoints, Effect.gen(function* () {
126
+ const sql = yield* SqlClient.SqlClient;
127
+ const table = sql(options?.checkpointsTable ?? "cqrs_checkpoints");
128
+ yield* sql `
129
+ CREATE TABLE IF NOT EXISTS ${table} (
130
+ view_model TEXT PRIMARY KEY,
131
+ position INTEGER NOT NULL
132
+ )
133
+ `.pipe(Effect.mapError(storeError("initialize")));
134
+ return Checkpoints.of({
135
+ get: (viewModel) => sql `SELECT position FROM ${table} WHERE view_model = ${viewModel}`.pipe(Effect.map(([row]) => row?.position ?? 0), Effect.mapError(storeError("checkpoint"))),
136
+ set: (viewModel, position) => sql `
137
+ INSERT INTO ${table} (view_model, position) VALUES (${viewModel}, ${position})
138
+ ON CONFLICT (view_model) DO UPDATE SET position = excluded.position
139
+ `.pipe(Effect.asVoid, Effect.mapError(storeError("checkpoint")))
140
+ });
141
+ }));
142
+ //# sourceMappingURL=SqliteEventStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqliteEventStore.js","sourceRoot":"","sources":["../src/SqliteEventStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The read side: projections of events into whatever storage a query needs.
3
+ *
4
+ * Moleculer-cqrs pushes events to view models over the broker, so an event sent
5
+ * while a service is down is lost until someone replays. Here each view model
6
+ * pulls from the store from its own checkpoint, so it can't miss anything, and
7
+ * delivery is at least once: a handler can see an event again after a crash and
8
+ * should be idempotent.
9
+ */
10
+ import { Context, Effect, Layer, Option, Schema } from "effect";
11
+ import type { DateTime } from "effect";
12
+ import type * as Aggregate from "./Aggregate.ts";
13
+ import { Checkpoints } from "./Checkpoints.ts";
14
+ import { EventStore, type EventStoreError } from "./EventStore.ts";
15
+ declare const UnknownViewModel_base: Schema.Class<UnknownViewModel, Schema.TaggedStruct<"UnknownViewModel", {
16
+ readonly name: Schema.String;
17
+ }>, import("effect/Cause").YieldableError>;
18
+ export declare class UnknownViewModel extends UnknownViewModel_base {
19
+ }
20
+ declare const ViewModelError_base: Schema.Class<ViewModelError, Schema.TaggedStruct<"ViewModelError", {
21
+ readonly viewModel: Schema.String;
22
+ /** The event being processed, `0` when the failure happened outside any event. */
23
+ readonly position: Schema.Int;
24
+ readonly cause: Schema.Defect;
25
+ }>, import("effect/Cause").YieldableError>;
26
+ export declare class ViewModelError extends ViewModelError_base {
27
+ }
28
+ export type Handlers<Event extends Aggregate.Tagged> = {
29
+ readonly [Tag in Event["_tag"]]?: (event: Extract<Event, {
30
+ readonly _tag: Tag;
31
+ }>, meta: Aggregate.EventMeta) => Effect.Effect<unknown, any, any>;
32
+ };
33
+ type HandlerError<H> = {
34
+ readonly [K in keyof H]-?: NonNullable<H[K]> extends (...args: any) => Effect.Effect<any, infer E, any> ? E : never;
35
+ }[keyof H];
36
+ type HandlerContext<H> = {
37
+ readonly [K in keyof H]-?: NonNullable<H[K]> extends (...args: any) => Effect.Effect<any, any, infer R> ? R : never;
38
+ }[keyof H];
39
+ export interface ViewModel<out Name extends string, out E, out R> {
40
+ readonly name: Name;
41
+ readonly aggregates: ReadonlyArray<Aggregate.Any>;
42
+ readonly handlers: Record<string, (event: never, meta: Aggregate.EventMeta) => Effect.Effect<unknown, E, R>>;
43
+ /** Clears everything the view model stored, before a replay rebuilds it. */
44
+ readonly dispose: Effect.Effect<void, E, R>;
45
+ }
46
+ export type Any = ViewModel<string, any, any>;
47
+ export type Context<V extends Any> = V extends ViewModel<any, any, infer R> ? R : never;
48
+ /** Handlers are keyed by event tag, and events without one are skipped. */
49
+ export declare const make: <const Name extends string, const Aggregates extends ReadonlyArray<Aggregate.Any>, const H extends Handlers<Aggregate.Event<Aggregates[number]>>, E = never, R = never>(options: {
50
+ readonly name: Name;
51
+ readonly aggregates: Aggregates;
52
+ readonly handlers: H;
53
+ readonly dispose: Effect.Effect<void, E, R>;
54
+ }) => ViewModel<Name, E | HandlerError<H>, R | HandlerContext<H>>;
55
+ export interface ReplayOptions {
56
+ readonly viewModels: ReadonlyArray<string>;
57
+ readonly startTime?: DateTime.Utc | undefined;
58
+ readonly finishTime?: DateTime.Utc | undefined;
59
+ }
60
+ export interface ReplayResult {
61
+ readonly viewModels: ReadonlyArray<string>;
62
+ readonly eventCount: number;
63
+ }
64
+ export interface Status {
65
+ readonly name: string;
66
+ readonly position: number;
67
+ /** The failure the view model is retrying, while it is stalled on one. */
68
+ readonly lastError: Option.Option<ViewModelError>;
69
+ }
70
+ declare const ViewModels_base: Context.ServiceClass<ViewModels, "effect-cqrs/ViewModels", {
71
+ /**
72
+ * Disposes the named view models and rebuilds them from their events. A time
73
+ * window limits which events are replayed: the view model then resumes from
74
+ * the present and events outside the window stay out of it.
75
+ */
76
+ replay(options: ReplayOptions): Effect.Effect<ReplayResult, UnknownViewModel | ViewModelError>;
77
+ /**
78
+ * Waits until every view model has processed `position`, defaulting to the
79
+ * latest event. Pair it with `CommandResult.position` to read your own writes.
80
+ * A view model stalled on a failure doesn't catch up, so consider a timeout.
81
+ */
82
+ caughtUp(position?: number): Effect.Effect<void, EventStoreError>;
83
+ readonly status: Effect.Effect<ReadonlyArray<Status>>;
84
+ }>;
85
+ export declare class ViewModels extends ViewModels_base {
86
+ }
87
+ /** Starts the view models in the background for as long as the layer lives. */
88
+ export declare const layer: <const VMs extends ReadonlyArray<Any>>(viewModels: VMs) => Layer.Layer<ViewModels, EventStoreError, EventStore | Checkpoints | Context<VMs[number]>>;
89
+ export {};
90
+ //# sourceMappingURL=ViewModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewModel.d.ts","sourceRoot":"","sources":["../src/ViewModel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * The read side: projections of events into whatever storage a query needs.
3
+ *
4
+ * Moleculer-cqrs pushes events to view models over the broker, so an event sent
5
+ * while a service is down is lost until someone replays. Here each view model
6
+ * pulls from the store from its own checkpoint, so it can't miss anything, and
7
+ * delivery is at least once: a handler can see an event again after a crash and
8
+ * should be idempotent.
9
+ */
10
+ import { Context, Duration, Effect, Layer, Option, Ref, Schedule, Schema, Semaphore, Stream, SubscriptionRef } from "effect";
11
+ import { makeEventDecoder } from "./AggregateService.js";
12
+ import * as Causation from "./Causation.js";
13
+ import { Checkpoints } from "./Checkpoints.js";
14
+ import { EventStore } from "./EventStore.js";
15
+ export class UnknownViewModel extends Schema.TaggedError()("UnknownViewModel", {
16
+ name: Schema.String
17
+ }) {
18
+ }
19
+ export class ViewModelError extends Schema.TaggedError()("ViewModelError", {
20
+ viewModel: Schema.String,
21
+ /** The event being processed, `0` when the failure happened outside any event. */
22
+ position: Schema.Int,
23
+ cause: Schema.Defect()
24
+ }) {
25
+ }
26
+ /** Handlers are keyed by event tag, and events without one are skipped. */
27
+ export const make = (options) => options;
28
+ export class ViewModels extends Context.Service()("effect-cqrs/ViewModels") {
29
+ }
30
+ // Stalled view models keep their place so events are never processed out of order.
31
+ const retryDelay = (attempt) => Duration.millis(Math.min(100 * 2 ** attempt, 30_000));
32
+ // Following the log moves a few events at a time, and a checkpoint for each of those
33
+ // moves is a commit per view model per event. Saving less often only means that a
34
+ // crash repeats more, which handlers already have to cope with.
35
+ const checkpointInterval = 500;
36
+ // How far behind a view model can be and still read the tail everyone shares. It is
37
+ // one page of the store, so a shared read never has to page.
38
+ const tailSize = 500;
39
+ /**
40
+ * Reads the end of the log once for all view models. When they are caught up they
41
+ * wake on the same append and want the same few events, so each reading them for
42
+ * itself is most of what a view model costs.
43
+ */
44
+ const makeTail = (store, viewModels) => {
45
+ const filter = {
46
+ aggregateNames: Array.from(new Set(viewModels.flatMap((viewModel) => viewModel.aggregates.map(({ name }) => name)))),
47
+ eventTypes: Array.from(new Set(viewModels.flatMap((viewModel) => Object.keys(viewModel.handlers))))
48
+ };
49
+ const decoded = new WeakMap();
50
+ let last;
51
+ return {
52
+ read: (afterPosition, latest) => Effect.suspend(() => last?.afterPosition === afterPosition && last.latest === latest
53
+ ? Effect.succeed(last.events)
54
+ : Stream.runCollect(store.read({ ...filter, afterPosition })).pipe(Effect.tap((events) => Effect.sync(() => void (last = { afterPosition, latest, events }))))),
55
+ decoded
56
+ };
57
+ };
58
+ const makeRunner = Effect.fnUntraced(function* (viewModel, tail) {
59
+ const store = yield* EventStore;
60
+ const checkpoints = yield* Checkpoints;
61
+ const context = yield* Effect.context();
62
+ const provide = (effect) => Effect.provideContext(effect, context);
63
+ const semaphore = yield* Semaphore.make(1);
64
+ const name = viewModel.name;
65
+ const fail = (position) => (cause) => new ViewModelError({ viewModel: name, position, cause });
66
+ const checkpoint = yield* checkpoints.get(name);
67
+ const position = yield* SubscriptionRef.make(checkpoint);
68
+ const saved = yield* Ref.make(checkpoint);
69
+ const lastError = yield* Ref.make(Option.none());
70
+ const stall = yield* Ref.make({ position: -1, attempts: 0 });
71
+ const decoders = new Map(viewModel.aggregates.map((aggregate) => [aggregate.name, makeEventDecoder(aggregate)]));
72
+ const filter = {
73
+ aggregateNames: Array.from(decoders.keys()),
74
+ eventTypes: Object.keys(viewModel.handlers)
75
+ };
76
+ const wants = (stored) => decoders.has(stored.aggregateName) && Object.hasOwn(viewModel.handlers, stored.type);
77
+ const decode = (stored) => decoders.get(stored.aggregateName)(stored);
78
+ // Events from the tail are the same objects for everyone, so the first to decode one
79
+ // does it for the rest.
80
+ const decodeShared = (stored) => tail.decoded.has(stored)
81
+ ? Effect.succeed(tail.decoded.get(stored))
82
+ : decode(stored).pipe(Effect.tap((event) => Effect.sync(() => void tail.decoded.set(stored, event))));
83
+ const handle = (decodeEvent) => (stored) => Effect.gen(function* () {
84
+ const event = yield* decodeEvent(stored);
85
+ // Commands the handler runs record this event as their cause.
86
+ yield* viewModel.handlers[stored.type](event, stored).pipe(Causation.causedBy(stored));
87
+ }).pipe(Effect.mapError(fail(stored.position)), Effect.catchDefect((defect) => Effect.fail(fail(stored.position)(defect))));
88
+ const save = (to) => checkpoints.set(name, to).pipe(Effect.mapError(fail(0)), Effect.andThen(Ref.set(saved, to)));
89
+ const advance = (to) => Ref.get(saved).pipe(Effect.flatMap((last) => to < last || to - last >= checkpointInterval ? save(to) : Effect.void), Effect.andThen(SubscriptionRef.set(position, to)));
90
+ // Finalizers run last to first, so this one sees the fiber below already stopped.
91
+ yield* Effect.addFinalizer(() => SubscriptionRef.get(position).pipe(Effect.flatMap(save), Effect.ignore));
92
+ // A crash repeats at most what came after the last saved checkpoint: two batches.
93
+ const processBatch = (batch, decodeEvent) => Effect.forEach(batch, handle(decodeEvent), { discard: true }).pipe(Effect.andThen(advance(batch[batch.length - 1].position)));
94
+ const consume = (eventFilter, onBatch) => store.read(eventFilter).pipe(Stream.mapError(fail(0)), Stream.runForEachArray((batch) => processBatch(batch, decode).pipe(Effect.andThen(onBatch(batch.length)))));
95
+ const follow = (afterPosition, latest) => tail.read(afterPosition, latest).pipe(Effect.mapError(fail(0)), Effect.map((events) => events.filter(wants)), Effect.flatMap((events) => events.length === 0 ? Effect.void : processBatch(events, decodeShared)));
96
+ // Moves past the end of the log even when the last events were not for this view model.
97
+ const settle = (head) => SubscriptionRef.get(position).pipe(Effect.flatMap((current) => current < head ? advance(head) : Effect.void));
98
+ const catchUp = (latest) => Effect.gen(function* () {
99
+ const afterPosition = yield* SubscriptionRef.get(position);
100
+ if (latest <= afterPosition)
101
+ return;
102
+ if (latest - afterPosition <= tailSize)
103
+ yield* follow(afterPosition, latest);
104
+ else
105
+ yield* consume({ ...filter, afterPosition }, () => Effect.void);
106
+ yield* settle(latest);
107
+ }).pipe(semaphore.withPermit, provide);
108
+ // Failing again at the same place doubles the delay. Having moved on means that
109
+ // stall is over, so the next one starts from the shortest delay.
110
+ const backOff = (error) => Effect.gen(function* () {
111
+ const current = yield* SubscriptionRef.get(position);
112
+ const attempt = yield* Ref.modify(stall, (last) => {
113
+ const attempts = last.position === current ? last.attempts + 1 : 0;
114
+ return [attempts, { position: current, attempts }];
115
+ });
116
+ yield* Ref.set(lastError, Option.some(error));
117
+ yield* Effect.logError(`View model ${name} failed, retrying`, error);
118
+ yield* Effect.sleep(retryDelay(attempt));
119
+ });
120
+ yield* store.changes.pipe(Stream.filterEffect((latest) => Effect.map(SubscriptionRef.get(position), (current) => latest > current)), Stream.runForEach((latest) => catchUp(latest).pipe(Effect.tapError(backOff), Effect.retry(Schedule.forever), Effect.andThen(Ref.set(lastError, Option.none())))), Effect.forkScoped);
121
+ const replay = (range) => Effect.gen(function* () {
122
+ // The checkpoint is cleared first: a crash in between then replays over
123
+ // old data, which is safe for idempotent handlers, instead of leaving a
124
+ // disposed view model that resumes from a stale position.
125
+ yield* advance(0);
126
+ yield* viewModel.dispose.pipe(Effect.mapError(fail(0)));
127
+ const head = yield* store.head.pipe(Effect.mapError(fail(0)));
128
+ const count = yield* Ref.make(0);
129
+ yield* consume({ ...filter, ...range }, (size) => Ref.update(count, (n) => n + size));
130
+ yield* settle(head);
131
+ return yield* Ref.get(count);
132
+ }).pipe(semaphore.withPermit, provide);
133
+ const caughtUp = (target) => SubscriptionRef.changes(position).pipe(Stream.filter((current) => current >= target), Stream.runHead, Effect.asVoid);
134
+ const status = Effect.gen(function* () {
135
+ return {
136
+ name,
137
+ position: yield* SubscriptionRef.get(position),
138
+ lastError: yield* Ref.get(lastError)
139
+ };
140
+ });
141
+ return { name, replay, caughtUp, status };
142
+ });
143
+ /** Starts the view models in the background for as long as the layer lives. */
144
+ export const layer = (viewModels) => Layer.effect(ViewModels, Effect.gen(function* () {
145
+ const store = yield* EventStore;
146
+ const tail = makeTail(store, viewModels);
147
+ const runners = yield* Effect.forEach(viewModels, (viewModel) => makeRunner(viewModel, tail));
148
+ const replay = Effect.fn("ViewModels.replay")(function* (options) {
149
+ const selected = yield* Effect.forEach(options.viewModels, (name) => {
150
+ const runner = runners.find((runner) => runner.name === name);
151
+ return runner === undefined ? new UnknownViewModel({ name }) : Effect.succeed(runner);
152
+ });
153
+ const counts = yield* Effect.forEach(selected, (runner) => runner.replay({ startTime: options.startTime, finishTime: options.finishTime }), { concurrency: "unbounded" });
154
+ return {
155
+ viewModels: options.viewModels,
156
+ eventCount: counts.reduce((total, count) => total + count, 0)
157
+ };
158
+ });
159
+ const caughtUp = Effect.fn("ViewModels.caughtUp")(function* (position) {
160
+ const target = position ?? (yield* store.head);
161
+ yield* Effect.forEach(runners, (runner) => runner.caughtUp(target), {
162
+ concurrency: "unbounded",
163
+ discard: true
164
+ });
165
+ });
166
+ return ViewModels.of({
167
+ replay,
168
+ caughtUp,
169
+ status: Effect.forEach(runners, (runner) => runner.status)
170
+ });
171
+ }));
172
+ //# sourceMappingURL=ViewModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewModel.js","sourceRoot":"","sources":["../src/ViewModel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export * as Aggregate from "./Aggregate.ts";
2
+ export * as AggregateService from "./AggregateService.ts";
3
+ export * as Causation from "./Causation.ts";
4
+ export * as Checkpoints from "./Checkpoints.ts";
5
+ export * as EventStore from "./EventStore.ts";
6
+ export * as EventVersions from "./EventVersions.ts";
7
+ export * as Fixture from "./Fixture.ts";
8
+ export * as Metadata from "./Metadata.ts";
9
+ export * as PostgresEventStore from "./PostgresEventStore.ts";
10
+ export * as Saga from "./Saga.ts";
11
+ export * as SqliteEventStore from "./SqliteEventStore.ts";
12
+ export * as ViewModel from "./ViewModel.ts";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAC7D,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export * as Aggregate from "./Aggregate.js";
2
+ export * as AggregateService from "./AggregateService.js";
3
+ export * as Causation from "./Causation.js";
4
+ export * as Checkpoints from "./Checkpoints.js";
5
+ export * as EventStore from "./EventStore.js";
6
+ export * as EventVersions from "./EventVersions.js";
7
+ export * as Fixture from "./Fixture.js";
8
+ export * as Metadata from "./Metadata.js";
9
+ export * as PostgresEventStore from "./PostgresEventStore.js";
10
+ export * as Saga from "./Saga.js";
11
+ export * as SqliteEventStore from "./SqliteEventStore.js";
12
+ export * as ViewModel from "./ViewModel.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAC7D,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA"}