Skip to content

Latest commit

 

History

History
191 lines (135 loc) · 5.9 KB

Specification.md

File metadata and controls

191 lines (135 loc) · 5.9 KB

EventStore

Reading

Expects a StreamQuery and an optional starting SequenceNumber and returns an EventStream.

Note

The EventStore should also allow for backwards iteration on the EventStream in order to support cursor based pagination.

Writing

Expects a set of Events and an AppendCondition and returns the last appended SequenceNumber.

API

A potential interface of the EventStore (pseudo-code):

EventStore {
  read(query: StreamQuery, options?: ReadOptions): EventStream
  append(events: Events|Event, condition: AppendCondition): void
}

ReadOptions

An optional parameter to EventStore.read() that allows for cursor-based pagination of events. It has two parameters:

  • backwards a flag that, if set to true, returns the events in descending order (default: false)
  • from an optional SequenceNumber to start streaming events from (depending on the backwards flag this is either a minimum or maximum sequence number of the resulting stream)
ReadOptions {
  from?: SequenceNumber
  backwards: bool
}

StreamQuery

The StreamQuery describes constraints that must be matched by Events in the EventStore It effectively allows for filtering events by their type and/or tags

  • It MAY contain a set of StreamQuery Criteria – a StreamQuery with an empty criteria set is considered a "wildcard" query, i.e. it matches all events

Note

All criteria of a StreamQuery are merged into a logical disjunction, so events match the query if they match the first OR the second criterion...

StreamQuery Criterion

Each criterion of a StreamQuery allows to target events by their type and/or tags

Note

event type filters of a single criterion are merged into a logical disjunction, so events match the criterion if they match ANY of the specified types tags are merged into a logical conjunction, so events match the criterion if they are tagged with ALL specified tags

Example StreamQuery

The following example query would match events that are either...

  • ...of type EventType1 OR EventType2
  • ...tagged foo:bar AND baz:foos
  • ...of type EventType2 OR EventType3 AND tagged foo:barAND foo:baz
{
  "criteria": [
    {
      "event_types": ["EventType1", "EventType2"]
    },
    {
      "tags": ["foo:bar", "baz:foos"]
    },
    {
      "event_types": ["EventType2", "EventType3"],
      "tags": ["foo:bar", "foo:baz"]
    }
  ]
}

SequenceNumber

When an Event is appended to the EventStore a SequenceNumber is assigned to it.

It...

  • MUST be unique for one EventStore
  • MUST be monotonic increasing
  • MUST have an allowed minimum value of 1
  • CAN contain gaps
  • SHOULD have a reasonably high maximum value (depending on programming language and environment)

EventStream

When reading from the EventStore an EventStream is returned.

It...

  • It MUST be iterable
  • It MUST return an EventEnvelope for every iteration
  • It CAN include new events if they occur during iteration
  • Individual EventEnvelope instances MAY be converted during iteration for performance optimization
  • Batches of events MAY be loaded from the underlying storage at once for performance optimization

EventEnvelope

Each item in the EventStream is an EventEnvelope that consists of the underlying event and metadata, like the SequenceNumber that was added during the append() call.

It...

  • It MUST contain the SequenceNumber
  • It MUST contain the Event
  • It CAN include more fields, like timestamps or metadata

EventEnvelope example

{
    "event": {
        "type": "SomeEventType",
        "data": "{\"some\":\"data\"}",
        "tags": ["type1:value1", "type2:value2"]
    },
    "sequence_number": 1234,
    "recorded_at": "2024-12-10 14:02:40"
}

Events

A set of Event instances that is passed to the append() method of the EventStore

It...

  • MUST not be empty
  • MUST be iterable, each iteration returning an Event

Event

  • It MUST contain an EventType
  • It MUST contain EventData
  • It MAY contain Tags
  • It MAY contain further fields, like metadata

Potential serialization format

{
    "type": "SomeEventType",
    "data": "{\"some\":\"data\"}",
    "tags": ["key1:value1", "key1:value2"]
}

EventType

String based type of the event

  • It MUST satisfy the regular expression ^[\w\.\:\-]{1,200}$

EventData

String based, opaque payload of an Event

  • It SHOULD have a reasonable large enough maximum length (depending on language and environment)
  • It MAY contain JSON
  • It MAY be serialized into an empty string

Tags

A set of Tag instances.

  • It MUST contain at least one Tag
  • It SHOULD not contain multiple Tags with the same value

Tag

A Tag can add domain specific metadata to an event allowing for custom partitioning

Note

Usually a tag represents a concept of the domain, e.g. the type and id of an entity like product:p123

  • It MUST satisfy the regular expression /^[[:alnum:]\-\_\:]{1,150}

AppendCondition

ExpectedHighestSequenceNumber

Can either represent an instance of SequenceNumber Or one of: