Skip to content

Event Store API

This page defines the minimal event store API required to support Command Context Consistency. The API is language- and technology-agnostic. It defines observable behavior, data structures, and consistency semantics. It does not prescribe a database, storage engine, serialization format, programming language, transport protocol, or implementation architecture.

API Overview

The Event Store API requires three operations:

append(new_events) -> append_result
query(event_query) -> query_result
append_if(new_events, context_query, expected_context_version) -> append_result | conditional_append_conflict

append commits new events.

query reads committed event records and returns query metadata.

append_if commits new events only when the command context has not changed.

These operations define the minimum observable behavior required for Command Context Consistency. Implementations may provide additional operational features, but those features must not change the contract defined here.

Data Structures

NewEvent

NewEvent
  event_type
  payload

Rules:

  • event_type identifies the kind of event.
  • payload contains the recorded event data.
  • A NewEvent has no sequence_number.
  • A NewEvent becomes an EventRecord only after a successful append.

EventRecord

EventRecord
  sequence_number
  occurred_at
  event_type
  payload

Rules:

  • sequence_number is the global committed position.
  • sequence_number defines committed order.
  • occurred_at is metadata.
  • occurred_at does not define committed order.

EventFilter

EventFilter
  event_types
  payload_predicates

Rules:

  • event_types is optional.
  • payload_predicates is optional.
  • If event_types is omitted, event type is unconstrained.
  • If event_types is explicitly empty, that filter matches no event type.
  • If payload_predicates is omitted, payload is unconstrained.
  • If payload_predicates is explicitly empty, that filter matches no payload predicate.

EventQuery

EventQuery
  filters
  min_sequence_number

Rules:

  • filters is optional.
  • min_sequence_number is optional.
  • If filters is omitted or empty, the query matches all committed event records.
  • min_sequence_number is an exclusive read cursor.
  • min_sequence_number affects returned records only.

The absence of filters does not mean no result. It means all records.

A query that matches all records can be large. Implementations may provide batching, limits, streaming, or named replay operations for operational safety. These mechanisms must not change the matching meaning of the query.

QueryResult

QueryResult
  event_records
  last_returned_sequence_number
  current_context_version

Rules:

  • event_records are returned in ascending sequence_number order.
  • last_returned_sequence_number is derived from returned records.
  • current_context_version is derived from the full matching context.
  • current_context_version ignores min_sequence_number.
  • These values must stay separate.

If event_records is empty, last_returned_sequence_number is absent.

If no records match the full query context, current_context_version is absent.

A query can return no records while still having a current_context_version when min_sequence_number filters out all returned records but the full context contains matching records.

AppendResult

AppendResult
  first_sequence_number
  last_sequence_number
  committed_count

Rules:

  • Describes one committed append batch.
  • Does not represent a context version.

ConditionalAppendConflict

ConditionalAppendConflict
  expected_context_version
  actual_context_version

Rules:

  • Returned when append_if detects that the command context changed.
  • expected_context_version and actual_context_version may be absent when the compared context is empty on one side.
  • No event is committed on conflict.

append

append(new_events) -> append_result

Input

new_events is a non-empty list of NewEvent.

Each submitted event must contain a valid event_type and payload.

Submitted events must not contain committed metadata such as sequence_number or occurred_at.

Success

A successful append:

  • commits all submitted events as one batch.
  • assigns global sequence numbers.
  • returns AppendResult.

The returned AppendResult describes the committed batch and the sequence range assigned to that batch.

Failure

Empty input is invalid.

Invalid events are rejected.

Backend failure is reported as backend_failure.

Guarantees

No partial commit.

One successful append creates one consecutive sequence range.

Failed append does not commit events.

Failed append does not consume sequence numbers later successful commits would observe.

query

query(event_query) -> query_result

Input

event_query selects committed event records.

The query may select all records, records matching one or more filters, records after a read cursor, or records matching filters after a read cursor.

Success

A successful query:

  • returns QueryResult.
  • returned records are ordered by ascending sequence_number.

Important distinction

last_returned_sequence_number describes only returned records.

current_context_version describes the full matching context.

min_sequence_number affects returned records only.

min_sequence_number does not affect current_context_version.

Failure

Invalid query shape is rejected.

Backend failure is reported as backend_failure.

Example

query:
  filters:
    - event_types: [tool_registered, tool_checked_out]
      payload_predicates:
        - tool_id: tool_1
  min_sequence_number: 40

For this query:

  • returned records have sequence_number > 40.
  • current_context_version is calculated without applying min_sequence_number.

append_if

append_if(new_events, context_query, expected_context_version)
  -> append_result | conditional_append_conflict

Input

new_events is a non-empty list of NewEvent.

context_query selects the facts relevant to the command decision.

expected_context_version is the context version observed before the command produced new events.

expected_context_version is the context version observed from a previous query.

If the previous query had no matching context, expected_context_version is absent.

Success

Store calculates actual context version from context_query.

Calculation ignores min_sequence_number.

If actual equals expected, append succeeds.

Returns AppendResult.

The actual context version may be absent when the context query matches no committed records.

An absent actual context version matches an absent expected context version.

An absent actual context version does not match a present expected context version.

A present actual context version does not match an absent expected context version.

Conflict

If actual differs from expected, append is rejected.

Returns ConditionalAppendConflict.

No event is committed.

No partial batch is committed.

Failed conditional append does not consume sequence numbers later successful commits would observe.

Guarantees

Consistency check and append are one atomic operation.

context_query defines the consistency boundary.

No aggregate, stream, subject, or entity boundary is required by the API.

Event Query Semantics

Element Semantics
filters omitted matches all records
filters: [] matches all records
multiple filters OR across filters
event_types omitted event type unconstrained
event_types: [] this filter matches no event type
multiple event_types OR across event types
payload_predicates omitted payload unconstrained
payload_predicates: [] this filter matches no payload predicate
multiple payload_predicates OR across payload predicates
event type and payload in same filter AND between constraints
min_sequence_number omitted no read cursor
min_sequence_number present exclusive read cursor

A record matches an EventQuery when it matches at least one EventFilter.

A record matches an EventFilter when the event type constraint matches, if present, and the payload predicate constraint matches, if present.

Payload predicate semantics:

  • scalar values match by equality.
  • objects match recursively by subset.
  • arrays match when every predicate element is contained somewhere in the payload array.
  • extra keys in the event payload are allowed.
  • extra array elements in the event payload are allowed.

Sequence Semantics

  • sequence numbers are global.
  • sequence numbers are monotonically increasing.
  • sequence numbers define committed order.
  • one committed append batch receives one consecutive sequence range.
  • occurred_at does not define order.
  • failed append and failed conditional append do not partially commit.
  • failed append and failed conditional append do not consume sequence numbers later successful commits would observe.

Error Semantics

empty_append

empty_append is reported when append or append_if receives no new events.

No event is committed.

conditional_append_conflict

conditional_append_conflict is returned when the actual context version differs from the expected context version.

The result includes the expected and actual context versions.

invalid_event

invalid_event is reported when a submitted NewEvent is malformed or contains fields that belong only to committed records.

No event from the submitted batch is committed.

invalid_query

invalid_query is reported when an EventQuery or EventFilter has an invalid shape.

The store does not execute an invalid query.

backend_failure

backend_failure is reported when the store cannot complete the requested operation because of a storage or execution failure.

The API must not report backend failure as a successful append, query, or conditional conflict.

Compliance Checklist

Append

  • rejects empty append.
  • commits non-empty batch atomically.
  • assigns consecutive sequence range.
  • returns append result.
  • failed append commits nothing.

Query

  • returns records ordered by ascending sequence number.
  • applies min_sequence_number exclusively.
  • separates last_returned_sequence_number from current_context_version.
  • ignores min_sequence_number for current_context_version.
  • implements filter semantics exactly.

Append_if

  • computes context version from full context query.
  • ignores min_sequence_number for conflict detection.
  • commits only when expected and actual context versions match.
  • returns conflict with expected and actual versions on mismatch.
  • failed conditional append commits nothing.