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.
Expects a set of Events and an AppendCondition and returns the last appended SequenceNumber.
A potential interface of the EventStore
(pseudo-code):
EventStore {
read(query: StreamQuery, options?: ReadOptions): EventStream
append(events: Events|Event, condition: AppendCondition): void
}
An optional parameter to EventStore.read()
that allows for cursor-based pagination of events.
It has two parameters:
backwards
a flag that, if set totrue
, returns the events in descending order (default:false
)from
an optional SequenceNumber to start streaming events from (depending on thebackwards
flag this is either a minimum or maximum sequence number of the resulting stream)
ReadOptions {
from?: SequenceNumber
backwards: bool
}
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...
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
The following example query would match events that are either...
- ...of type
EventType1
OREventType2
- ...tagged
foo:bar
ANDbaz:foos
- ...of type
EventType2
OREventType3
AND taggedfoo:bar
ANDfoo:baz
{
"criteria": [
{
"event_types": ["EventType1", "EventType2"]
},
{
"tags": ["foo:bar", "baz:foos"]
},
{
"event_types": ["EventType2", "EventType3"],
"tags": ["foo:bar", "foo:baz"]
}
]
}
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)
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
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
{
"event": {
"type": "SomeEventType",
"data": "{\"some\":\"data\"}",
"tags": ["type1:value1", "type2:value2"]
},
"sequence_number": 1234,
"recorded_at": "2024-12-10 14:02:40"
}
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
- It MUST contain an EventType
- It MUST contain EventData
- It MAY contain Tags
- It MAY contain further fields, like metadata
{
"type": "SomeEventType",
"data": "{\"some\":\"data\"}",
"tags": ["key1:value1", "key1:value2"]
}
String based type of the event
- It MUST satisfy the regular expression
^[\w\.\:\-]{1,200}$
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
A set of Tag instances.
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}
- It MUST contain a StreamQuery
- It MUST contain a ExpectedHighestSequenceNumber
Can either represent an instance of SequenceNumber Or one of:
NONE
– No event must match the specified StreamQueryANY
– Any event matches (= wildcard AppendCondition)