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.
- package/LICENSE +21 -0
- package/README.md +326 -2
- package/dist/Aggregate.d.ts +127 -0
- package/dist/Aggregate.d.ts.map +1 -0
- package/dist/Aggregate.js +59 -0
- package/dist/Aggregate.js.map +1 -0
- package/dist/AggregateService.d.ts +154 -0
- package/dist/AggregateService.d.ts.map +1 -0
- package/dist/AggregateService.js +407 -0
- package/dist/AggregateService.js.map +1 -0
- package/dist/Causation.d.ts +34 -0
- package/dist/Causation.d.ts.map +1 -0
- package/dist/Causation.js +32 -0
- package/dist/Causation.js.map +1 -0
- package/dist/Checkpoints.d.ts +19 -0
- package/dist/Checkpoints.d.ts.map +1 -0
- package/dist/Checkpoints.js +18 -0
- package/dist/Checkpoints.js.map +1 -0
- package/dist/EventStore.d.ts +132 -0
- package/dist/EventStore.d.ts.map +1 -0
- package/dist/EventStore.js +156 -0
- package/dist/EventStore.js.map +1 -0
- package/dist/EventVersions.d.ts +64 -0
- package/dist/EventVersions.d.ts.map +1 -0
- package/dist/EventVersions.js +55 -0
- package/dist/EventVersions.js.map +1 -0
- package/dist/Fixture.d.ts +106 -0
- package/dist/Fixture.d.ts.map +1 -0
- package/dist/Fixture.js +149 -0
- package/dist/Fixture.js.map +1 -0
- package/dist/Metadata.d.ts +42 -0
- package/dist/Metadata.d.ts.map +1 -0
- package/dist/Metadata.js +39 -0
- package/dist/Metadata.js.map +1 -0
- package/dist/PostgresEventStore.d.ts +30 -0
- package/dist/PostgresEventStore.d.ts.map +1 -0
- package/dist/PostgresEventStore.js +153 -0
- package/dist/PostgresEventStore.js.map +1 -0
- package/dist/Saga.d.ts +105 -0
- package/dist/Saga.d.ts.map +1 -0
- package/dist/Saga.js +97 -0
- package/dist/Saga.js.map +1 -0
- package/dist/SqliteEventStore.d.ts +25 -0
- package/dist/SqliteEventStore.d.ts.map +1 -0
- package/dist/SqliteEventStore.js +142 -0
- package/dist/SqliteEventStore.js.map +1 -0
- package/dist/ViewModel.d.ts +90 -0
- package/dist/ViewModel.d.ts.map +1 -0
- package/dist/ViewModel.js +172 -0
- package/dist/ViewModel.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/package.json +89 -3
- package/src/Aggregate.ts +205 -0
- package/src/AggregateService.ts +705 -0
- package/src/Causation.ts +48 -0
- package/src/Checkpoints.ts +23 -0
- package/src/EventStore.ts +270 -0
- package/src/EventVersions.ts +104 -0
- package/src/Fixture.ts +298 -0
- package/src/Metadata.ts +52 -0
- package/src/PostgresEventStore.ts +199 -0
- package/src/Saga.ts +176 -0
- package/src/SqliteEventStore.ts +196 -0
- package/src/ViewModel.ts +350 -0
- package/src/index.ts +12 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs an aggregate against the {@link EventStore}: the `command`, `read-model`
|
|
3
|
+
* and `history` actions of moleculer-cqrs, as a typed service.
|
|
4
|
+
*/
|
|
5
|
+
import { Cause, type DateTime, Effect, Schema, type Scope } from "effect";
|
|
6
|
+
import { KeyValueStore } from "effect/persistence";
|
|
7
|
+
import * as Aggregate from "./Aggregate.ts";
|
|
8
|
+
import { ConcurrencyConflict, EventStore, type EventStoreError, type StoredEvent } from "./EventStore.ts";
|
|
9
|
+
import { Metadata } from "./Metadata.ts";
|
|
10
|
+
declare const UnknownCommand_base: Schema.Class<UnknownCommand, Schema.TaggedStruct<"UnknownCommand", {
|
|
11
|
+
readonly aggregateName: Schema.String;
|
|
12
|
+
readonly type: Schema.String;
|
|
13
|
+
}>, Cause.YieldableError>;
|
|
14
|
+
export declare class UnknownCommand extends UnknownCommand_base {
|
|
15
|
+
}
|
|
16
|
+
declare const InvalidCommand_base: Schema.Class<InvalidCommand, Schema.TaggedStruct<"InvalidCommand", {
|
|
17
|
+
readonly aggregateName: Schema.String;
|
|
18
|
+
readonly type: Schema.String;
|
|
19
|
+
readonly message: Schema.String;
|
|
20
|
+
}>, Cause.YieldableError>;
|
|
21
|
+
export declare class InvalidCommand extends InvalidCommand_base {
|
|
22
|
+
}
|
|
23
|
+
declare const EventDecodeError_base: Schema.Class<EventDecodeError, Schema.TaggedStruct<"EventDecodeError", {
|
|
24
|
+
readonly aggregateName: Schema.String;
|
|
25
|
+
readonly aggregateId: Schema.String;
|
|
26
|
+
readonly version: Schema.Int;
|
|
27
|
+
readonly type: Schema.String;
|
|
28
|
+
readonly schemaVersion: Schema.Int;
|
|
29
|
+
readonly message: Schema.String;
|
|
30
|
+
}>, Cause.YieldableError>;
|
|
31
|
+
/**
|
|
32
|
+
* A stored event doesn't match the schema of its version, or was written in a
|
|
33
|
+
* version the aggregate doesn't declare. Old shapes are declared with `EventVersions`.
|
|
34
|
+
*/
|
|
35
|
+
export declare class EventDecodeError extends EventDecodeError_base {
|
|
36
|
+
}
|
|
37
|
+
export type ReadError = EventStoreError | EventDecodeError;
|
|
38
|
+
/** How a command can fail, on top of the errors its own handler declares. */
|
|
39
|
+
export type CommandFailure = ConcurrencyConflict | InvalidCommand | ReadError;
|
|
40
|
+
export interface CommandResult<Event> {
|
|
41
|
+
readonly aggregateName: string;
|
|
42
|
+
readonly aggregateId: string;
|
|
43
|
+
/** The stream version after the command. */
|
|
44
|
+
readonly version: number;
|
|
45
|
+
/** The store position of the last event, to pass to `ViewModels.caughtUp`. */
|
|
46
|
+
readonly position: number;
|
|
47
|
+
readonly events: ReadonlyArray<Event>;
|
|
48
|
+
/** Recorded on the events, see `Causation`. */
|
|
49
|
+
readonly correlationId: string;
|
|
50
|
+
}
|
|
51
|
+
export interface HistoryEntry<Event> {
|
|
52
|
+
readonly version: number;
|
|
53
|
+
readonly timestamp: DateTime.Utc;
|
|
54
|
+
readonly type: string;
|
|
55
|
+
readonly correlationId: string | null;
|
|
56
|
+
readonly causationId: string | null;
|
|
57
|
+
readonly metadata: Metadata;
|
|
58
|
+
readonly payload?: Event;
|
|
59
|
+
}
|
|
60
|
+
export interface AggregateService<A extends Aggregate.Any> {
|
|
61
|
+
readonly aggregate: A;
|
|
62
|
+
/** One typed function per command. */
|
|
63
|
+
readonly commands: {
|
|
64
|
+
readonly [K in Aggregate.CommandName<A>]: (aggregateId: string, payload: Aggregate.Payload<A, K>) => Effect.Effect<CommandResult<Aggregate.Event<A>>, Aggregate.CommandError<A, K> | CommandFailure>;
|
|
65
|
+
};
|
|
66
|
+
/** Runs a `{ aggregateId, type, payload }` envelope received as JSON from outside the process. */
|
|
67
|
+
readonly dispatch: (input: unknown) => Effect.Effect<CommandResult<Aggregate.Event<A>>, Aggregate.CommandError<A> | CommandFailure | UnknownCommand>;
|
|
68
|
+
/** Materializes the state on the fly, optionally as it was before `finishTime`. */
|
|
69
|
+
readonly readModel: (aggregateId: string, options?: {
|
|
70
|
+
readonly finishTime?: DateTime.Utc | undefined;
|
|
71
|
+
}) => Effect.Effect<Aggregate.State<A>, ReadError>;
|
|
72
|
+
/** Lists the events of one aggregate, with their decoded payload when `payload` is set. */
|
|
73
|
+
readonly history: (aggregateId: string, options?: {
|
|
74
|
+
readonly payload?: boolean | undefined;
|
|
75
|
+
readonly startTime?: DateTime.Utc | undefined;
|
|
76
|
+
readonly finishTime?: DateTime.Utc | undefined;
|
|
77
|
+
}) => Effect.Effect<ReadonlyArray<HistoryEntry<Aggregate.Event<A>>>, ReadError>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Decodes a stored event with the schema of the aggregate it belongs to. An
|
|
81
|
+
* event stored in an older version is upcast to the current one on the way.
|
|
82
|
+
*/
|
|
83
|
+
export declare const makeEventDecoder: <A extends Aggregate.Any>(aggregate: A) => (stored: StoredEvent) => Effect.Effect<Aggregate.Event<A>, EventDecodeError>;
|
|
84
|
+
/** Encodes events into the JSON form the store keeps, in the current version of their schema. */
|
|
85
|
+
export declare const makeEventEncoder: <A extends Aggregate.Any>(aggregate: A) => (event: Aggregate.Event<A>) => Effect.Effect<{
|
|
86
|
+
type: any;
|
|
87
|
+
payload: Schema.Json;
|
|
88
|
+
schemaVersion: number;
|
|
89
|
+
}, never, never>;
|
|
90
|
+
/** Saves the state of each aggregate every so often, so a cold start replays only what came after. */
|
|
91
|
+
export interface SnapshotOptions<State> {
|
|
92
|
+
/** How many events to fold after a snapshot before saving the next one. */
|
|
93
|
+
readonly every: number;
|
|
94
|
+
/** How the state is stored. It is encoded as JSON. */
|
|
95
|
+
readonly schema: Schema.Codec<State, any, never, never>;
|
|
96
|
+
/**
|
|
97
|
+
* Part of every key. Change it whenever the state or the projection changes,
|
|
98
|
+
* so that snapshots of the old state are ignored and rebuilt from the log.
|
|
99
|
+
* Defaults to `1`.
|
|
100
|
+
*/
|
|
101
|
+
readonly version?: string | number | undefined;
|
|
102
|
+
}
|
|
103
|
+
export interface Options {
|
|
104
|
+
/** How many times a command is re-decided after losing a race. Defaults to 5. */
|
|
105
|
+
readonly maxRetries?: number | undefined;
|
|
106
|
+
/** How many aggregates keep their state in memory between commands. Defaults to 10,000. */
|
|
107
|
+
readonly stateCacheSize?: number | undefined;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The names of the metrics every aggregate service records through Effect's
|
|
111
|
+
* `Metric` module. Each series carries an `aggregate` attribute with the
|
|
112
|
+
* aggregate's name. Read them with `Metric.snapshot`, or send them on with an
|
|
113
|
+
* exporter such as `OtlpMetrics` from `effect/observability`.
|
|
114
|
+
*/
|
|
115
|
+
export declare const MetricNames: {
|
|
116
|
+
/**
|
|
117
|
+
* Timer: how long a command took, including its wait for an earlier command on
|
|
118
|
+
* the same stream, by `command` and `outcome`. The outcome is `success`, the tag
|
|
119
|
+
* of the error, `defect` or `interrupted`. A payload that doesn't decode, or a
|
|
120
|
+
* type the aggregate doesn't know, fails before this point and isn't recorded.
|
|
121
|
+
*/
|
|
122
|
+
readonly commandDuration: "cqrs_command_duration";
|
|
123
|
+
/**
|
|
124
|
+
* Counter: where loading the current state found a state to start from, by
|
|
125
|
+
* `source`: `memory`, `snapshot`, or `replay` when it starts from the first event.
|
|
126
|
+
*/
|
|
127
|
+
readonly stateLoads: "cqrs_state_loads_total";
|
|
128
|
+
/** Histogram: how many events a load read from the store on top of that state. */
|
|
129
|
+
readonly eventsReplayed: "cqrs_events_replayed";
|
|
130
|
+
/** Gauge: how many states are remembered in memory. */
|
|
131
|
+
readonly stateCacheSize: "cqrs_state_cache_size";
|
|
132
|
+
/** Counter: states dropped from memory to make room for others, see `stateCacheSize`. */
|
|
133
|
+
readonly stateCacheEvictions: "cqrs_state_cache_evictions_total";
|
|
134
|
+
/** Counter: snapshots saved, by `result`: `success` or `failure`. */
|
|
135
|
+
readonly snapshotSaves: "cqrs_snapshot_saves_total";
|
|
136
|
+
/** Counter: snapshots that couldn't be read, so the stream was replayed instead. */
|
|
137
|
+
readonly snapshotReadFailures: "cqrs_snapshot_read_failures_total";
|
|
138
|
+
/** Counter: appends that lost a race with a writer in another process. Each is retried until `maxRetries` runs out. */
|
|
139
|
+
readonly concurrencyConflicts: "cqrs_concurrency_conflicts_total";
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Builds the service for one aggregate. With `snapshots` it saves states through
|
|
143
|
+
* a `KeyValueStore` from `effect/persistence`, which has layers for memory, files,
|
|
144
|
+
* SQL and the browser's `localStorage`. Saves run in the background, and closing
|
|
145
|
+
* the scope waits for the ones still running.
|
|
146
|
+
*/
|
|
147
|
+
export declare const make: {
|
|
148
|
+
<A extends Aggregate.Any>(aggregate: A, options: Options & {
|
|
149
|
+
readonly snapshots: SnapshotOptions<Aggregate.State<A>>;
|
|
150
|
+
}): Effect.Effect<AggregateService<A>, never, EventStore | KeyValueStore.KeyValueStore | Scope.Scope | Aggregate.Context<A>>;
|
|
151
|
+
<A extends Aggregate.Any>(aggregate: A, options?: Options): Effect.Effect<AggregateService<A>, never, EventStore | Aggregate.Context<A>>;
|
|
152
|
+
};
|
|
153
|
+
export {};
|
|
154
|
+
//# sourceMappingURL=AggregateService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AggregateService.d.ts","sourceRoot":"","sources":["../src/AggregateService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs an aggregate against the {@link EventStore}: the `command`, `read-model`
|
|
3
|
+
* and `history` actions of moleculer-cqrs, as a typed service.
|
|
4
|
+
*/
|
|
5
|
+
import { Array as Arr, Cause, Context, Duration, Effect, Exit, FiberSet, Metric, Option, Predicate, Schedule, Schema, Semaphore, Stream } from "effect";
|
|
6
|
+
import { KeyValueStore } from "effect/persistence";
|
|
7
|
+
import * as Aggregate from "./Aggregate.js";
|
|
8
|
+
import { CurrentCausation } from "./Causation.js";
|
|
9
|
+
import { ConcurrencyConflict, EventStore } from "./EventStore.js";
|
|
10
|
+
import * as EventVersions from "./EventVersions.js";
|
|
11
|
+
import { CurrentMetadata, Metadata } from "./Metadata.js";
|
|
12
|
+
export class UnknownCommand extends Schema.TaggedError()("UnknownCommand", {
|
|
13
|
+
aggregateName: Schema.String,
|
|
14
|
+
type: Schema.String
|
|
15
|
+
}) {
|
|
16
|
+
}
|
|
17
|
+
export class InvalidCommand extends Schema.TaggedError()("InvalidCommand", {
|
|
18
|
+
aggregateName: Schema.String,
|
|
19
|
+
type: Schema.String,
|
|
20
|
+
message: Schema.String
|
|
21
|
+
}) {
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A stored event doesn't match the schema of its version, or was written in a
|
|
25
|
+
* version the aggregate doesn't declare. Old shapes are declared with `EventVersions`.
|
|
26
|
+
*/
|
|
27
|
+
export class EventDecodeError extends Schema.TaggedError()("EventDecodeError", {
|
|
28
|
+
aggregateName: Schema.String,
|
|
29
|
+
aggregateId: Schema.String,
|
|
30
|
+
version: Schema.Int,
|
|
31
|
+
type: Schema.String,
|
|
32
|
+
schemaVersion: Schema.Int,
|
|
33
|
+
message: Schema.String
|
|
34
|
+
}) {
|
|
35
|
+
}
|
|
36
|
+
const CommandEnvelope = Schema.Struct({
|
|
37
|
+
aggregateId: Schema.NonEmptyString,
|
|
38
|
+
type: Schema.String,
|
|
39
|
+
payload: Schema.Unknown,
|
|
40
|
+
/** Continues a correlation started elsewhere, see `Causation`. */
|
|
41
|
+
correlationId: Schema.optional(Schema.String),
|
|
42
|
+
causationId: Schema.optional(Schema.String),
|
|
43
|
+
/** Recorded on top of the current metadata, see `Metadata`. */
|
|
44
|
+
metadata: Schema.optional(Metadata)
|
|
45
|
+
});
|
|
46
|
+
const versionsOf = (aggregate) => Object.entries(aggregate.versions).flatMap(([tag, versions]) => versions === undefined ? [] : [[tag, versions]]);
|
|
47
|
+
/**
|
|
48
|
+
* Decodes a stored event with the schema of the aggregate it belongs to. An
|
|
49
|
+
* event stored in an older version is upcast to the current one on the way.
|
|
50
|
+
*/
|
|
51
|
+
export const makeEventDecoder = (aggregate) => {
|
|
52
|
+
const decodeCurrent = Schema.decodeUnknownEffect(Schema.toCodecJson(aggregate.events));
|
|
53
|
+
const upcasters = new Map(versionsOf(aggregate).map(([tag, versions]) => [tag, {
|
|
54
|
+
current: EventVersions.current(versions),
|
|
55
|
+
decoders: EventVersions.decoders(tag, versions).map((decoder) => Schema.decodeUnknownEffect(decoder))
|
|
56
|
+
}]));
|
|
57
|
+
return (stored) => {
|
|
58
|
+
const fail = (message) => new EventDecodeError({
|
|
59
|
+
aggregateName: stored.aggregateName,
|
|
60
|
+
aggregateId: stored.aggregateId,
|
|
61
|
+
version: stored.version,
|
|
62
|
+
type: stored.type,
|
|
63
|
+
schemaVersion: stored.schemaVersion,
|
|
64
|
+
message
|
|
65
|
+
});
|
|
66
|
+
const upcaster = upcasters.get(stored.type);
|
|
67
|
+
const current = upcaster?.current ?? 1;
|
|
68
|
+
const decode = stored.schemaVersion === current ? decodeCurrent : upcaster?.decoders[stored.schemaVersion - 1];
|
|
69
|
+
if (decode === undefined) {
|
|
70
|
+
return Effect.fail(fail(`${aggregate.name} knows ${stored.type} up to schema version ${current}`));
|
|
71
|
+
}
|
|
72
|
+
return decode(stored.payload).pipe(Effect.mapError((error) => fail(error.message)));
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/** Encodes events into the JSON form the store keeps, in the current version of their schema. */
|
|
76
|
+
export const makeEventEncoder = (aggregate) => {
|
|
77
|
+
const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(aggregate.events));
|
|
78
|
+
const versions = new Map(versionsOf(aggregate).map(([tag, versions]) => [tag, EventVersions.current(versions)]));
|
|
79
|
+
return (event) => encode(event).pipe(
|
|
80
|
+
// Events come from the aggregate's own handlers, so this is a programming error.
|
|
81
|
+
Effect.orDie, Effect.map((payload) => ({
|
|
82
|
+
type: event._tag,
|
|
83
|
+
payload: payload,
|
|
84
|
+
schemaVersion: versions.get(event._tag) ?? 1
|
|
85
|
+
})));
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The names of the metrics every aggregate service records through Effect's
|
|
89
|
+
* `Metric` module. Each series carries an `aggregate` attribute with the
|
|
90
|
+
* aggregate's name. Read them with `Metric.snapshot`, or send them on with an
|
|
91
|
+
* exporter such as `OtlpMetrics` from `effect/observability`.
|
|
92
|
+
*/
|
|
93
|
+
export const MetricNames = {
|
|
94
|
+
/**
|
|
95
|
+
* Timer: how long a command took, including its wait for an earlier command on
|
|
96
|
+
* the same stream, by `command` and `outcome`. The outcome is `success`, the tag
|
|
97
|
+
* of the error, `defect` or `interrupted`. A payload that doesn't decode, or a
|
|
98
|
+
* type the aggregate doesn't know, fails before this point and isn't recorded.
|
|
99
|
+
*/
|
|
100
|
+
commandDuration: "cqrs_command_duration",
|
|
101
|
+
/**
|
|
102
|
+
* Counter: where loading the current state found a state to start from, by
|
|
103
|
+
* `source`: `memory`, `snapshot`, or `replay` when it starts from the first event.
|
|
104
|
+
*/
|
|
105
|
+
stateLoads: "cqrs_state_loads_total",
|
|
106
|
+
/** Histogram: how many events a load read from the store on top of that state. */
|
|
107
|
+
eventsReplayed: "cqrs_events_replayed",
|
|
108
|
+
/** Gauge: how many states are remembered in memory. */
|
|
109
|
+
stateCacheSize: "cqrs_state_cache_size",
|
|
110
|
+
/** Counter: states dropped from memory to make room for others, see `stateCacheSize`. */
|
|
111
|
+
stateCacheEvictions: "cqrs_state_cache_evictions_total",
|
|
112
|
+
/** Counter: snapshots saved, by `result`: `success` or `failure`. */
|
|
113
|
+
snapshotSaves: "cqrs_snapshot_saves_total",
|
|
114
|
+
/** Counter: snapshots that couldn't be read, so the stream was replayed instead. */
|
|
115
|
+
snapshotReadFailures: "cqrs_snapshot_read_failures_total",
|
|
116
|
+
/** Counter: appends that lost a race with a writer in another process. Each is retried until `maxRetries` runs out. */
|
|
117
|
+
concurrencyConflicts: "cqrs_concurrency_conflicts_total"
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Builds each series once with its attributes: a metric's key is cached, while
|
|
121
|
+
* `Metric.withAttributes` would compute it again on every update.
|
|
122
|
+
*/
|
|
123
|
+
const makeMetrics = (aggregateName) => {
|
|
124
|
+
const attributes = { aggregate: aggregateName };
|
|
125
|
+
const counter = (name, description, extra) => Metric.counter(name, { description, incremental: true, attributes: { ...attributes, ...extra } });
|
|
126
|
+
const loads = (source) => counter(MetricNames.stateLoads, "Where loading an aggregate found a state to start from", { source });
|
|
127
|
+
const saves = (result) => counter(MetricNames.snapshotSaves, "Snapshots saved", { result });
|
|
128
|
+
const durations = new Map();
|
|
129
|
+
return {
|
|
130
|
+
loadedFromMemory: loads("memory"),
|
|
131
|
+
loadedFromSnapshot: loads("snapshot"),
|
|
132
|
+
loadedFromReplay: loads("replay"),
|
|
133
|
+
eventsReplayed: Metric.histogram(MetricNames.eventsReplayed, {
|
|
134
|
+
description: "Events read from the store to bring a state up to date",
|
|
135
|
+
attributes,
|
|
136
|
+
boundaries: [0, 1, 5, 10, 50, 100, 500, 1000, 5000, 10000]
|
|
137
|
+
}),
|
|
138
|
+
cacheSize: Metric.gauge(MetricNames.stateCacheSize, { description: "States remembered in memory", attributes }),
|
|
139
|
+
evictions: counter(MetricNames.stateCacheEvictions, "States dropped from memory to make room"),
|
|
140
|
+
snapshotSaved: saves("success"),
|
|
141
|
+
snapshotFailed: saves("failure"),
|
|
142
|
+
snapshotReadFailures: counter(MetricNames.snapshotReadFailures, "Snapshots that couldn't be read"),
|
|
143
|
+
conflicts: counter(MetricNames.concurrencyConflicts, "Appends that lost a race with another writer"),
|
|
144
|
+
duration: (command, outcome) => {
|
|
145
|
+
let byOutcome = durations.get(command);
|
|
146
|
+
if (byOutcome === undefined)
|
|
147
|
+
durations.set(command, byOutcome = new Map());
|
|
148
|
+
let timer = byOutcome.get(outcome);
|
|
149
|
+
if (timer === undefined) {
|
|
150
|
+
timer = Metric.timer(MetricNames.commandDuration, {
|
|
151
|
+
description: "How long commands take",
|
|
152
|
+
attributes: { ...attributes, command, outcome }
|
|
153
|
+
});
|
|
154
|
+
byOutcome.set(outcome, timer);
|
|
155
|
+
}
|
|
156
|
+
return timer;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
/** Counts one occurrence and returns `value`, in a single step rather than an effect per update. */
|
|
161
|
+
const recording = (counter, value) => Effect.contextWith((context) => {
|
|
162
|
+
counter.updateUnsafe(1, context);
|
|
163
|
+
return Effect.succeed(value);
|
|
164
|
+
});
|
|
165
|
+
const outcomeOf = (exit) => {
|
|
166
|
+
if (Exit.isSuccess(exit))
|
|
167
|
+
return "success";
|
|
168
|
+
const error = Cause.findErrorOption(exit.cause);
|
|
169
|
+
if (Option.isSome(error)) {
|
|
170
|
+
const value = error.value;
|
|
171
|
+
return Predicate.hasProperty(value, "_tag") && Predicate.isString(value._tag) ? value._tag : "error";
|
|
172
|
+
}
|
|
173
|
+
return Cause.hasInterruptsOnly(exit.cause) ? "interrupted" : "defect";
|
|
174
|
+
};
|
|
175
|
+
/** Remembers recent states, dropping the least recently used. A `Map` iterates in insertion order. */
|
|
176
|
+
const makeStateCache = (capacity) => {
|
|
177
|
+
const entries = new Map();
|
|
178
|
+
return {
|
|
179
|
+
size: () => entries.size,
|
|
180
|
+
get: (aggregateId) => {
|
|
181
|
+
const entry = entries.get(aggregateId);
|
|
182
|
+
if (entry !== undefined) {
|
|
183
|
+
entries.delete(aggregateId);
|
|
184
|
+
entries.set(aggregateId, entry);
|
|
185
|
+
}
|
|
186
|
+
return entry;
|
|
187
|
+
},
|
|
188
|
+
/** Returns whether another state was dropped to make room. */
|
|
189
|
+
set: (aggregateId, loaded) => {
|
|
190
|
+
if (capacity === 0 || (entries.get(aggregateId)?.position ?? 0) > loaded.position)
|
|
191
|
+
return false;
|
|
192
|
+
entries.delete(aggregateId);
|
|
193
|
+
entries.set(aggregateId, loaded);
|
|
194
|
+
if (entries.size <= capacity)
|
|
195
|
+
return false;
|
|
196
|
+
entries.delete(entries.keys().next().value);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
/** Runs effects with the same key one at a time. A key only exists while it is in use. */
|
|
202
|
+
const makeKeyedLock = () => {
|
|
203
|
+
const locks = new Map();
|
|
204
|
+
return (key) => (effect) => Effect.acquireUseRelease(Effect.sync(() => {
|
|
205
|
+
const lock = locks.get(key) ?? { semaphore: Semaphore.makeUnsafe(1), users: 0 };
|
|
206
|
+
lock.users++;
|
|
207
|
+
locks.set(key, lock);
|
|
208
|
+
return lock;
|
|
209
|
+
}), (lock) => lock.semaphore.withPermit(effect), (lock) => Effect.sync(() => {
|
|
210
|
+
if (--lock.users === 0)
|
|
211
|
+
locks.delete(key);
|
|
212
|
+
}));
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Gives other fibers a turn once in a while. `Effect.forEach` with `concurrency`
|
|
216
|
+
* carries on as long as items don't suspend, and a store on `node:sqlite` never does,
|
|
217
|
+
* so a bulk run would finish before any view model ran. Yielding per command is
|
|
218
|
+
* expensive, so it only happens when a millisecond has passed, using real time
|
|
219
|
+
* because this is about the CPU. The clock restarts after the yield rather than
|
|
220
|
+
* before it, so commands started meanwhile suspend too and the loop lets go.
|
|
221
|
+
*/
|
|
222
|
+
const makeTimeSlice = () => {
|
|
223
|
+
let started = performance.now();
|
|
224
|
+
return Effect.suspend(() => performance.now() - started < 1
|
|
225
|
+
? Effect.void
|
|
226
|
+
: Effect.andThen(Effect.yieldNow, Effect.sync(() => void (started = performance.now()))));
|
|
227
|
+
};
|
|
228
|
+
// Only a rival outside this service can cause a conflict, so give it room to finish.
|
|
229
|
+
const conflictBackoff = Schedule.exponential("10 millis").pipe(Schedule.jittered);
|
|
230
|
+
const makeSnapshots = (aggregateName, options, metrics) => Effect.gen(function* () {
|
|
231
|
+
const store = KeyValueStore.toSchemaStore(yield* KeyValueStore.KeyValueStore, Schema.Struct({ version: Schema.Int, position: Schema.Int, state: options.schema }));
|
|
232
|
+
const key = (aggregateId) => `effect-cqrs/snapshot/${JSON.stringify([aggregateName, String(options.version ?? 1), aggregateId])}`;
|
|
233
|
+
// A snapshot is only a shortcut, so a failure to read or save one falls back to the log.
|
|
234
|
+
const get = (aggregateId) => store.get(key(aggregateId)).pipe(Effect.map(Option.map((snapshot) => ({ ...snapshot, snapshotVersion: snapshot.version }))), Effect.catch((error) => Metric.update(metrics.snapshotReadFailures, 1).pipe(Effect.andThen(Effect.logWarning(`Ignoring the ${aggregateName} snapshot of ${aggregateId}`, error)), Effect.as(Option.none()))));
|
|
235
|
+
// Saves run beside the commands rather than in their way. Closing the service
|
|
236
|
+
// waits for the ones still running, so a clean shutdown keeps its last snapshots.
|
|
237
|
+
const saving = yield* FiberSet.make();
|
|
238
|
+
yield* Effect.addFinalizer(() => FiberSet.awaitEmpty(saving));
|
|
239
|
+
// Starts a save once `every` events have been folded since the last snapshot, and
|
|
240
|
+
// returns the state to remember. A failed save is not retried until `every` more.
|
|
241
|
+
const save = (aggregateId, loaded) => loaded.version - loaded.snapshotVersion < options.every
|
|
242
|
+
? Effect.succeed(loaded)
|
|
243
|
+
: FiberSet.run(saving, store.set(key(aggregateId), { version: loaded.version, position: loaded.position, state: loaded.state }).pipe(Effect.andThen(Metric.update(metrics.snapshotSaved, 1)), Effect.catch((error) => Effect.andThen(Metric.update(metrics.snapshotFailed, 1), Effect.logWarning(`Could not save the ${aggregateName} snapshot of ${aggregateId}`, error))))).pipe(Effect.as({ ...loaded, snapshotVersion: loaded.version }));
|
|
244
|
+
return { get, save };
|
|
245
|
+
});
|
|
246
|
+
const makeService = Effect.fnUntraced(function* (aggregate, options) {
|
|
247
|
+
const store = yield* EventStore;
|
|
248
|
+
const context = yield* Effect.context();
|
|
249
|
+
// Metadata provided where the service was built, such as the software version,
|
|
250
|
+
// reaches commands run where that layer isn't visible.
|
|
251
|
+
const builtWith = Context.get(context, CurrentMetadata);
|
|
252
|
+
const mergeMetadata = (current, given) => current === builtWith && given === undefined ? builtWith : { ...builtWith, ...current, ...given };
|
|
253
|
+
const maxRetries = options?.maxRetries ?? 5;
|
|
254
|
+
const aggregateName = aggregate.name;
|
|
255
|
+
const decodeEvent = makeEventDecoder(aggregate);
|
|
256
|
+
const encodeEvent = makeEventEncoder(aggregate);
|
|
257
|
+
const states = makeStateCache(options?.stateCacheSize ?? 10_000);
|
|
258
|
+
const lockStream = makeKeyedLock();
|
|
259
|
+
const timeSlice = makeTimeSlice();
|
|
260
|
+
const metrics = makeMetrics(aggregateName);
|
|
261
|
+
const snapshots = options?.snapshots === undefined
|
|
262
|
+
? undefined
|
|
263
|
+
: yield* makeSnapshots(aggregateName, options.snapshots, metrics);
|
|
264
|
+
const replay = (stream, initial, step) => stream.pipe(Stream.runFoldEffect(() => initial, (acc, stored) => Effect.map(decodeEvent(stored), (event) => step(acc, event, stored))));
|
|
265
|
+
const streamOf = (aggregateId, range) => store.read({ aggregateNames: [aggregateName], aggregateIds: [aggregateId], ...range });
|
|
266
|
+
const fold = (acc, event, stored) => ({
|
|
267
|
+
state: Aggregate.evolve(aggregate, acc.state, event, stored),
|
|
268
|
+
version: stored.version,
|
|
269
|
+
position: stored.position,
|
|
270
|
+
snapshotVersion: acc.snapshotVersion
|
|
271
|
+
});
|
|
272
|
+
const replayFrom = (start, aggregateId, finishTime) => replay(streamOf(aggregateId, { finishTime, afterPosition: start.position, afterVersion: start.version }), start, fold);
|
|
273
|
+
const initial = {
|
|
274
|
+
state: aggregate.initial,
|
|
275
|
+
version: 0,
|
|
276
|
+
position: 0,
|
|
277
|
+
snapshotVersion: 0
|
|
278
|
+
};
|
|
279
|
+
// Remembered states come first, as they are the most recent. Without one the
|
|
280
|
+
// last snapshot saves replaying the start of the stream.
|
|
281
|
+
const start = (aggregateId) => {
|
|
282
|
+
const remembered = states.get(aggregateId);
|
|
283
|
+
if (remembered !== undefined)
|
|
284
|
+
return recording(metrics.loadedFromMemory, remembered);
|
|
285
|
+
if (snapshots === undefined)
|
|
286
|
+
return recording(metrics.loadedFromReplay, initial);
|
|
287
|
+
return Effect.flatMap(snapshots.get(aggregateId), Option.match({
|
|
288
|
+
onNone: () => recording(metrics.loadedFromReplay, initial),
|
|
289
|
+
onSome: (snapshot) => recording(metrics.loadedFromSnapshot, snapshot)
|
|
290
|
+
}));
|
|
291
|
+
};
|
|
292
|
+
// Returns the state as remembered, with the version of a snapshot it just started.
|
|
293
|
+
// `read` is how many events the load read from the store, when one did.
|
|
294
|
+
const remember = (aggregateId, loaded, read) => (snapshots === undefined ? Effect.succeed(loaded) : snapshots.save(aggregateId, loaded)).pipe(Effect.tap((remembered) => Effect.contextWith((context) => {
|
|
295
|
+
const evicted = states.set(aggregateId, remembered);
|
|
296
|
+
if (read !== undefined)
|
|
297
|
+
metrics.eventsReplayed.updateUnsafe(read, context);
|
|
298
|
+
metrics.cacheSize.updateUnsafe(states.size(), context);
|
|
299
|
+
if (evicted)
|
|
300
|
+
metrics.evictions.updateUnsafe(1, context);
|
|
301
|
+
return Effect.void;
|
|
302
|
+
})));
|
|
303
|
+
// Streams only grow, so a remembered state plus what came after it is the current
|
|
304
|
+
// state, even if another process wrote those events. Versions have no gaps, so the
|
|
305
|
+
// difference between two is the number of events read.
|
|
306
|
+
const load = (aggregateId, finishTime) => finishTime === undefined
|
|
307
|
+
? Effect.flatMap(start(aggregateId), (from) => Effect.flatMap(replayFrom(from, aggregateId), (loaded) => remember(aggregateId, loaded, loaded.version - from.version)))
|
|
308
|
+
: replayFrom(initial, aggregateId, finishTime);
|
|
309
|
+
// Measures from the moment the command asks for its turn on the stream.
|
|
310
|
+
const timed = (type) => (effect) => Effect.clockWith((clock) => {
|
|
311
|
+
const started = clock.currentTimeNanosUnsafe();
|
|
312
|
+
return Effect.onExit(effect, (exit) => Effect.contextWith((context) => {
|
|
313
|
+
const elapsed = Duration.nanos(clock.currentTimeNanosUnsafe() - started);
|
|
314
|
+
metrics.duration(type, outcomeOf(exit)).updateUnsafe(elapsed, context);
|
|
315
|
+
return Effect.void;
|
|
316
|
+
}));
|
|
317
|
+
});
|
|
318
|
+
// Folds what a command just wrote into the remembered state, so the next command
|
|
319
|
+
// finds nothing new to read. The events are decoded from what was stored, as a
|
|
320
|
+
// replay would, so the state is the one a restart would rebuild. If they don't
|
|
321
|
+
// decode, the state isn't remembered and the next load reports the error.
|
|
322
|
+
const rememberWritten = (aggregateId, before, written) => Effect.forEach(written, decodeEvent).pipe(Effect.flatMap((events) => remember(aggregateId, events.reduce((acc, event, index) => fold(acc, event, written[index]), before))), Effect.ignore);
|
|
323
|
+
const decide = Effect.fnUntraced(function* (type, aggregateId, payload, causation, metadata) {
|
|
324
|
+
const loaded = yield* load(aggregateId);
|
|
325
|
+
const { position, state, version } = loaded;
|
|
326
|
+
const { handler } = aggregate.commands[type];
|
|
327
|
+
const events = Arr.ensure(yield* handler(state, payload, { aggregateId, version }));
|
|
328
|
+
const { correlationId } = causation;
|
|
329
|
+
if (events.length === 0) {
|
|
330
|
+
return { aggregateName, aggregateId, version, position, events, correlationId };
|
|
331
|
+
}
|
|
332
|
+
const stored = yield* store.append({
|
|
333
|
+
aggregateName,
|
|
334
|
+
aggregateId,
|
|
335
|
+
expectedVersion: version,
|
|
336
|
+
events: yield* Effect.forEach(events, encodeEvent),
|
|
337
|
+
...causation,
|
|
338
|
+
metadata
|
|
339
|
+
});
|
|
340
|
+
yield* rememberWritten(aggregateId, loaded, stored);
|
|
341
|
+
const last = stored[stored.length - 1];
|
|
342
|
+
return { aggregateName, aggregateId, version: last.version, position: last.position, events, correlationId };
|
|
343
|
+
});
|
|
344
|
+
// Commands on one stream take turns, so they can't race each other and only a
|
|
345
|
+
// rival in another process causes a conflict. A handler must not run a command on
|
|
346
|
+
// its own stream, which would wait for itself.
|
|
347
|
+
const execute = (type, aggregateId, payload, given) =>
|
|
348
|
+
// Read outside the service's own context, which would hide the caller's.
|
|
349
|
+
Effect.contextWith((callerContext) => {
|
|
350
|
+
const current = Context.get(callerContext, CurrentCausation);
|
|
351
|
+
const correlationId = given?.correlationId ?? current?.correlationId;
|
|
352
|
+
const causationId = given?.causationId ?? current?.causationId;
|
|
353
|
+
// Without a cause the command starts its own correlation.
|
|
354
|
+
const id = correlationId ?? causationId ?? crypto.randomUUID();
|
|
355
|
+
const causation = { correlationId: correlationId ?? id, causationId: causationId ?? id };
|
|
356
|
+
const metadata = mergeMetadata(Context.get(callerContext, CurrentMetadata), given?.metadata);
|
|
357
|
+
return decide(type, aggregateId, payload, causation, metadata).pipe(Effect.tapError((error) => error instanceof ConcurrencyConflict ? Metric.update(metrics.conflicts, 1) : Effect.void), Effect.retry({
|
|
358
|
+
times: maxRetries,
|
|
359
|
+
while: (error) => error instanceof ConcurrencyConflict,
|
|
360
|
+
schedule: conflictBackoff
|
|
361
|
+
}), Effect.provideContext(context), lockStream(aggregateId), (decided) => Effect.andThen(timeSlice, decided), timed(type), Effect.withSpan(`${aggregateName}.${type}`, {
|
|
362
|
+
attributes: { "aggregate.id": aggregateId, "cqrs.correlation_id": causation.correlationId }
|
|
363
|
+
}));
|
|
364
|
+
});
|
|
365
|
+
const decodePayload = (type, schema, input) => Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((error) => new InvalidCommand({ aggregateName, type, message: error.message })));
|
|
366
|
+
const commands = Object.fromEntries(Object.entries(aggregate.commands).map(([type, command]) => [
|
|
367
|
+
type,
|
|
368
|
+
(aggregateId, payload) => decodePayload(type, Schema.toType(command.payload), payload).pipe(Effect.flatMap((decoded) => execute(type, aggregateId, decoded)))
|
|
369
|
+
]));
|
|
370
|
+
const decodeEnvelope = Schema.decodeUnknownEffect(CommandEnvelope);
|
|
371
|
+
const dispatch = (input) => Effect.gen(function* () {
|
|
372
|
+
const envelope = yield* decodeEnvelope(input).pipe(Effect.mapError((error) => new InvalidCommand({ aggregateName, type: "unknown", message: error.message })));
|
|
373
|
+
if (!Object.hasOwn(aggregate.commands, envelope.type)) {
|
|
374
|
+
return yield* new UnknownCommand({ aggregateName, type: envelope.type });
|
|
375
|
+
}
|
|
376
|
+
const payload = yield* decodePayload(envelope.type, Schema.toCodecJson(aggregate.commands[envelope.type].payload), envelope.payload);
|
|
377
|
+
return yield* execute(envelope.type, envelope.aggregateId, payload, envelope);
|
|
378
|
+
});
|
|
379
|
+
const readModel = (aggregateId, options) => load(aggregateId, options?.finishTime).pipe(Effect.map(({ state }) => state), Effect.withSpan(`${aggregateName}.readModel`, { attributes: { "aggregate.id": aggregateId } }));
|
|
380
|
+
const history = (aggregateId, options) => replay(streamOf(aggregateId, { startTime: options?.startTime, finishTime: options?.finishTime }), [], (entries, event, stored) => {
|
|
381
|
+
entries.push({
|
|
382
|
+
version: stored.version,
|
|
383
|
+
timestamp: stored.timestamp,
|
|
384
|
+
type: stored.type,
|
|
385
|
+
correlationId: stored.correlationId,
|
|
386
|
+
causationId: stored.causationId,
|
|
387
|
+
metadata: stored.metadata,
|
|
388
|
+
...(options?.payload === true ? { payload: event } : {})
|
|
389
|
+
});
|
|
390
|
+
return entries;
|
|
391
|
+
}).pipe(Effect.withSpan(`${aggregateName}.history`, { attributes: { "aggregate.id": aggregateId } }));
|
|
392
|
+
return {
|
|
393
|
+
aggregate,
|
|
394
|
+
commands,
|
|
395
|
+
dispatch,
|
|
396
|
+
readModel,
|
|
397
|
+
history
|
|
398
|
+
};
|
|
399
|
+
});
|
|
400
|
+
/**
|
|
401
|
+
* Builds the service for one aggregate. With `snapshots` it saves states through
|
|
402
|
+
* a `KeyValueStore` from `effect/persistence`, which has layers for memory, files,
|
|
403
|
+
* SQL and the browser's `localStorage`. Saves run in the background, and closing
|
|
404
|
+
* the scope waits for the ones still running.
|
|
405
|
+
*/
|
|
406
|
+
export const make = makeService;
|
|
407
|
+
//# sourceMappingURL=AggregateService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AggregateService.js","sourceRoot":"","sources":["../src/AggregateService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why an event was recorded. Every event carries two ids:
|
|
3
|
+
*
|
|
4
|
+
* - `correlationId` is shared by everything that follows from one original
|
|
5
|
+
* request, across aggregates and view models.
|
|
6
|
+
* - `causationId` names what directly caused the command that recorded it: the
|
|
7
|
+
* request, or the event a view model or saga was reacting to.
|
|
8
|
+
*
|
|
9
|
+
* They travel in the fiber's context rather than in payloads. A command run
|
|
10
|
+
* outside any causation starts a new correlation with a fresh id. Inside a view
|
|
11
|
+
* model handler the event being handled is the cause, so a chain of reactions
|
|
12
|
+
* can be drawn as a tree without passing ids around by hand.
|
|
13
|
+
*/
|
|
14
|
+
import { Context, Effect } from "effect";
|
|
15
|
+
import { type StoredEvent } from "./EventStore.ts";
|
|
16
|
+
export interface Causation {
|
|
17
|
+
readonly correlationId: string;
|
|
18
|
+
readonly causationId: string;
|
|
19
|
+
}
|
|
20
|
+
/** The causation of commands run in this fiber, `undefined` outside any. */
|
|
21
|
+
export declare const CurrentCausation: Context.Reference<Causation | undefined>;
|
|
22
|
+
/** Runs an effect with this causation, for instance the id of an incoming HTTP request. */
|
|
23
|
+
export declare const withCausation: {
|
|
24
|
+
(causation: Causation): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
25
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>, causation: Causation): Effect.Effect<A, E, R>;
|
|
26
|
+
};
|
|
27
|
+
/** The causation of commands run in reaction to a stored event. */
|
|
28
|
+
export declare const fromEvent: (event: StoredEvent) => Causation;
|
|
29
|
+
/**
|
|
30
|
+
* Runs an effect in reaction to a stored event, so commands it runs record that
|
|
31
|
+
* event as their cause. View models do this for their handlers already.
|
|
32
|
+
*/
|
|
33
|
+
export declare const causedBy: (event: StoredEvent) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
34
|
+
//# sourceMappingURL=Causation.d.ts.map
|