Skip to content

The append-only log every aggregate and view model is built on.

Each event belongs to a stream, identified by aggregate name and id, and has two coordinates: a version that orders it inside its stream and a position that orders it across the whole store. Writers use the version for optimistic concurrency, readers use the position as a durable cursor.

Defined in: src/EventStore.ts:43

  • object & YieldableError<this>

new ConcurrencyConflict(…args): ConcurrencyConflict

Defined in: node_modules/.pnpm/effect@4.0.6/node_modules/effect/dist/Schema.d.ts:10979

…[props: object, options?: MakeOptions]

ConcurrencyConflict

Schema.TaggedError<ConcurrencyConflict>()("ConcurrencyConflict", { aggregateName: Schema.String, aggregateId: Schema.String, expectedVersion: Schema.Int }).constructor

readonly aggregateId: string = Schema.String

Defined in: src/EventStore.ts:45

Schema.TaggedError<ConcurrencyConflict>()("ConcurrencyConflict", { aggregateName: Schema.String, aggregateId: Schema.String, expectedVersion: Schema.Int }).aggregateId

readonly aggregateName: string = Schema.String

Defined in: src/EventStore.ts:44

Schema.TaggedError<ConcurrencyConflict>()("ConcurrencyConflict", { aggregateName: Schema.String, aggregateId: Schema.String, expectedVersion: Schema.Int }).aggregateName

readonly expectedVersion: number = Schema.Int

Defined in: src/EventStore.ts:46

Schema.TaggedError<ConcurrencyConflict>()("ConcurrencyConflict", { aggregateName: Schema.String, aggregateId: Schema.String, expectedVersion: Schema.Int }).expectedVersion


Defined in: src/EventStore.ts:139

  • Shape<"effect-cqrs/EventStore", { afterCommit?: <R>(effect) => Effect<void, never, R>; changes: Stream<number>; head: Effect<number, EventStoreError>; append(input): Effect<readonly object[], EventStoreError | ConcurrencyConflict>; read(filter?): Stream<{ aggregateId: string; aggregateName: string; causationId: string | null; correlationId: string | null; metadata: {[key: string]: Json; }; payload: Json; position: number; schemaVersion: number; timestamp: Utc; type: string; version: number; }, EventStoreError>; subscribe(filter?): Stream<{ aggregateId: string; aggregateName: string; causationId: string | null; correlationId: string | null; metadata: {[key: string]: Json; }; payload: Json; position: number; schemaVersion: number; timestamp: Utc; type: string; version: number; }, EventStoreError>; }, this>

new EventStore(_): EventStore

Defined in: node_modules/.pnpm/effect@4.0.6/node_modules/effect/dist/Context.d.ts:121

never

EventStore

Context.Service<EventStore, { /** Appends atomically, failing if the stream moved past `expectedVersion`. */ append(input: AppendInput): Effect.Effect<ReadonlyArray<StoredEvent>, ConcurrencyConflict | EventStoreError> /** Reads the events stored so far, ordered by position, then completes. */ read(filter?: EventFilter): Stream.Stream<StoredEvent, EventStoreError> /** Reads the events stored so far, then keeps following new ones. Never completes. */ subscribe(filter?: EventFilter): Stream.Stream<StoredEvent, EventStoreError> /** The position of the latest event, `0` when the store is empty. */ readonly head: Effect.Effect<number, EventStoreError> /** * Emits the current latest position, then each newer one. Consumers can treat an * element as “there may be something new”. Appends from this process are always * observed, appends from other processes when the backend announces them or the * store polls for them: see `MakeOptions`. */ readonly changes: Stream.Stream<number> /** * Runs an effect once what was just appended can be read by everyone: at once, * or when the caller’s transaction commits, and never if it rolls back. Services * that keep what they read or wrote in memory go through it, so they don’t keep * what a rollback undid. Missing on stores without transactions. */ readonly afterCommit?: (<R>(effect: Effect.Effect<void, never, R>) => Effect.Effect<void, never, R>) | undefined }>()(“effect-cqrs/EventStore”).constructor


Defined in: src/EventStore.ts:38

  • object & YieldableError<this>

new EventStoreError(…args): EventStoreError

Defined in: node_modules/.pnpm/effect@4.0.6/node_modules/effect/dist/Schema.d.ts:10979

…[props: object, options?: MakeOptions]

EventStoreError

Schema.TaggedError<EventStoreError>()("EventStoreError", { operation: Schema.String, cause: Schema.Defect() }).constructor

cause: unknown

Defined in: src/EventStore.ts:40

Schema.TaggedError<EventStoreError>()("EventStoreError", { operation: Schema.String, cause: Schema.Defect() }).cause

readonly operation: string = Schema.String

Defined in: src/EventStore.ts:39

Schema.TaggedError<EventStoreError>()("EventStoreError", { operation: Schema.String, cause: Schema.Defect() }).operation

Defined in: src/EventStore.ts:75

readonly aggregateId: string

Defined in: src/EventStore.ts:77

readonly aggregateName: string

Defined in: src/EventStore.ts:76

readonly optional causationId?: string

Defined in: src/EventStore.ts:82

readonly optional correlationId?: string

Defined in: src/EventStore.ts:81

readonly events: readonly NewEvent[]

Defined in: src/EventStore.ts:80

readonly expectedVersion: number

Defined in: src/EventStore.ts:79

The stream version the writer based its decision on, 0 for a new stream.

readonly optional metadata?: JsonObject

Defined in: src/EventStore.ts:84

Recorded on every event of the append. Defaults to {}.


Defined in: src/EventStore.ts:165

What a storage adapter implements. make builds the rest on top.

readonly optional afterCommit?: <R>(effect) => Effect<void, never, R>

Defined in: src/EventStore.ts:185

Runs an effect once an append made now can be read by everyone: at once, or when the caller’s transaction commits, and never if it rolls back. SqlClient.afterCommit is one. Without it appends are announced as soon as they return, which makes subscribers skip those of a transaction that is still open.

R

Effect<void, never, R>

Effect<void, never, R>

readonly append: (input) => Effect<readonly object[], EventStoreError | ConcurrencyConflict>

Defined in: src/EventStore.ts:166

Appends atomically, failing if the stream moved past expectedVersion.

AppendInput

Effect<readonly object[], EventStoreError | ConcurrencyConflict>

readonly optional changes?: Stream<number, EventStoreError, never>

Defined in: src/EventStore.ts:178

Head positions written by other processes, as the database announces them. It should start with the head once it listens, so nothing between two runs is missed. When it fails or ends, the store logs it and runs it again a second later.

readonly head: Effect<number, EventStoreError>

Defined in: src/EventStore.ts:172

readonly readBatch: (filter, afterPosition, limit) => Effect<readonly object[], EventStoreError>

Defined in: src/EventStore.ts:167

EventFilter

number

number

Effect<readonly object[], EventStoreError>


Defined in: src/EventStore.ts:121

Selects events by stream, type and time. Omitted fields match everything. startTime is inclusive and finishTime is exclusive, so adding one millisecond to a history timestamp selects up to and including that event.

readonly optional afterPosition?: number

Defined in: src/EventStore.ts:127

readonly optional afterVersion?: number

Defined in: src/EventStore.ts:132

For a filter on one stream: where to continue in it. Versions follow the stream’s own index, so resuming there costs the same however far the log has moved on.

readonly optional aggregateIds?: readonly string[]

Defined in: src/EventStore.ts:123

readonly optional aggregateNames?: readonly string[]

Defined in: src/EventStore.ts:122

readonly optional eventTypes?: readonly string[]

Defined in: src/EventStore.ts:124

readonly optional finishTime?: Utc

Defined in: src/EventStore.ts:126

readonly optional startTime?: Utc

Defined in: src/EventStore.ts:125


Defined in: src/EventStore.ts:188

readonly optional batchSize?: number

Defined in: src/EventStore.ts:190

Events per read. Defaults to 500.

readonly optional pollInterval?: Input

Defined in: src/EventStore.ts:195

Reads the head this often, to notice appends from other processes when the backend doesn’t announce them, or as a safety net when it does. Off by default.


Defined in: src/EventStore.ts:32

A schema change for a SQL table, with the table names filled in. Each module that keeps a table exports its migrations in the order to apply them, for teams that run them with their own tooling and turn off migrate.

readonly id: number

Defined in: src/EventStore.ts:33

readonly name: string

Defined in: src/EventStore.ts:34

readonly statements: readonly string[]

Defined in: src/EventStore.ts:35


Defined in: src/EventStore.ts:68

readonly payload: Json

Defined in: src/EventStore.ts:70

readonly optional schemaVersion?: number

Defined in: src/EventStore.ts:72

Defaults to 1.

readonly type: string

Defined in: src/EventStore.ts:69

StoredEvent = typeof StoredEvent.Type

Defined in: src/EventStore.ts:49

const groupCommit: (…args) => Effect<(input) => Effect<readonly object[], EventStoreError | ConcurrencyConflict>, never, Scope>

Defined in: src/EventStore.ts:331

Group commit for a backend. An append that finds nothing being written goes straight through appendGroup, in its own fiber. Appends that arrive meanwhile wait, then go together into the next call, which writes them in one transaction and returns an exit for each, in order: however many wait, they pay for one commit.

A group holds at most one append per stream, so they can’t conflict with each other, and at most maxEvents events unless a single append has more. An append left out waits for the next group, where it comes first, so appends to a stream are written in the order they came. Groups of waiting appends are written by a fiber of the Scope. An append interrupted while it waits is not written, while one interrupted once its group is being written may be: a caller that times out should retry with a commandId, which finds the first result if there is one. The others in its group are written and answered as if nothing happened.

…[appendGroup: (inputs) => Effect<readonly AppendExit[]>, options?: object]

Effect<(input) => Effect<readonly object[], EventStoreError | ConcurrencyConflict>, never, Scope>


const layerMemory: Layer.Layer<EventStore, EventStoreError>

Defined in: src/EventStore.ts:540

Keeps events in process memory. They do not survive a restart.


const make: (…args) => Effect<{ afterCommit?: <R>(effect) => Effect<void, never, R>; changes: Stream<number>; head: Effect<number, EventStoreError>; append(input): Effect<readonly object[], EventStoreError | ConcurrencyConflict>; read(filter?): Stream<{ aggregateId: string; aggregateName: string; causationId: string | null; correlationId: string | null; metadata: {[key: string]: Json; }; payload: Json; position: number; schemaVersion: number; timestamp: Utc; type: string; version: number; }, EventStoreError>; subscribe(filter?): Stream<{ aggregateId: string; aggregateName: string; causationId: string | null; correlationId: string | null; metadata: {[key: string]: Json; }; payload: Json; position: number; schemaVersion: number; timestamp: Utc; type: string; version: number; }, EventStoreError>; }, EventStoreError, Scope>

Defined in: src/EventStore.ts:208

Builds a store on a backend. Watching for appends from other processes takes fibers that live as long as the Scope, which Layer.effect provides.

…[backend: Backend, options?: MakeOptions]

Effect<{ afterCommit?: <R>(effect) => Effect<void, never, R>; changes: Stream<number>; head: Effect<number, EventStoreError>; append(input): Effect<readonly object[], EventStoreError | ConcurrencyConflict>; read(filter?): Stream<{ aggregateId: string; aggregateName: string; causationId: string | null; correlationId: string | null; metadata: {[key: string]: Json; }; payload: Json; position: number; schemaVersion: number; timestamp: Utc; type: string; version: number; }, EventStoreError>; subscribe(filter?): Stream<{ aggregateId: string; aggregateName: string; causationId: string | null; correlationId: string | null; metadata: {[key: string]: Json; }; payload: Json; position: number; schemaVersion: number; timestamp: Utc; type: string; version: number; }, EventStoreError>; }, EventStoreError, Scope>


const makeMemory: Effect.Effect<EventStore["Service"], EventStoreError>

Defined in: src/EventStore.ts:487


const StoredEvent: Struct<{ aggregateId: String; aggregateName: String; causationId: NullOr<String>; correlationId: NullOr<String>; metadata: $Record<String, Codec<Json, Json, never, never>>; payload: Codec<Json, Json, never, never>; position: Int; schemaVersion: Int; timestamp: DateTimeUtcFromMillis; type: String; version: Int; }>

Defined in: src/EventStore.ts:49

eventId(event): string

Defined in: src/EventStore.ts:92

Identifies an event wherever it is stored, as the causationId of what it causes: account/alice@4. Name and id are URI-encoded, so a / or @ inside one can’t make two events look alike.

Pick<StoredEvent, "aggregateName" | "aggregateId" | "version">

string


isOneStream(__namedParameters): boolean

Defined in: src/EventStore.ts:136

A filter on exactly one stream, which a store can read through that stream’s index.

EventFilter

boolean


toStored(input, event, index, position, timestamp): object

Defined in: src/EventStore.ts:96

The columns every adapter stores for a new event, with the defaults filled in.

AppendInput

NewEvent

number

number

Utc

readonly aggregateId: string = Schema.String

readonly aggregateName: string = Schema.String

readonly causationId: string | null

What directly caused the command that recorded the event: a request, a command or another event.

readonly correlationId: string | null

Shared by everything that follows from one original request.

readonly metadata: object = Schema.JsonObject

What the command carried beside its payload, see Metadata. Events from before metadata existed have {}.

[key: string]: Json

readonly payload: Json = Schema.Json

readonly position: number = Schema.Int

readonly schemaVersion: number = Schema.Int

The version of the event’s schema the payload was written with. Events from before versions existed are 1.

readonly timestamp: Utc = Schema.DateTimeUtcFromMillis

readonly type: string = Schema.String

readonly version: number = Schema.Int