## EventStore ### Reading Expects a [StreamQuery](#StreamQuery) and an optional starting [SequenceNumber](#SequenceNumber) and returns an [EventStream](#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](#Events) and an [AppendCondition](#AppendCondition) and returns the last appended [SequenceNumber](#SequenceNumber). #### Potential API ``` EventStore { read(query: StreamQuery, from?: SequenceNumber): EventStream readBackwards(query: StreamQuery, from?: SequenceNumber): EventStream append(events: Events, condition: AppendCondition): SequenceNumber } ``` ## StreamQuery The `StreamQuery` describes constraints that must be matched by [Event](#Event)s in the [EventStore](#EventStore). * It _MAY_ contain a set of [StreamQuery Criteria](#StreamQuery-Criterion) **Note:** All criteria of a StreamQuery are merged into a *logical disjunction*, so the example below matches all events, that match the first **OR** the second criterion. #### Potential serialization format ```json { "version": "1.0", "criteria": [{ "type": "EventTypes", "properties": { "event_types": ["EventType1", "EventType2"] } }, { "type": "Tags", "properties": { "tags": ["foo:bar", "baz:foos"], } }, { "type": "EventTypesAndTags", "properties": { "event_types": ["EventType2", "EventType3"], "tags": ["foo:bar", "foo:baz"], } }] } ``` ## StreamQuery Criterion In v1 the only supported criteria types are: * `Tags` – allows to target one or more [Tags](#Tags) * `EventTypes` – allows to target one or more [EventType](#EventType)s * `EventTypesAndTags` – allows to target one or more [Tags](#Tags) and one or more [EventType](#EventType)s ## SequenceNumber When an [Event](#Event) is appended to the [EventStore](#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](#EventStore) an `EventStream` is returned. It... * It _MUST_ be iterable * It _MUST_ return an [EventEnvelope](#EventEnvelope) for every iteration * It _CAN_ include new events if they occur during iteration * Individual [EventEnvelope](#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](#EventStream) is an `EventEnvelope` that consists of the underlying event and metadata, like the [SequenceNumber](#SequenceNumber) that was added during the `append()` call. ```json { "event": { "id": "15aaa216-4179-46d9-999a-75516e21a1c6", "type": "SomeEventType", "data": "{\"some\":\"data\"}" "tags": ["type1:value1", "type2:value2"] }, "sequence_number": 1234 } ``` ## Events A set of [Event](#Event) instances that is passed to the `append()` method of the [EventStore](#EventStore) It... * _MUST_ not be empty * _MUST_ be iterable, each iteration returning an [Event](#Event) ## Event * It _MUST_ contain a globally unique [EventId](#EventId) * It _MUST_ contain an [EventType](#EventType) * It _MUST_ contain [EventData](#EventData) * It _MAY_ contain [Tags](#Tags) #### Potential serialization format ```json { "id": "15aaa216-4179-46d9-999a-75516e21a1c6", "type": "SomeEventType", "data": "{\"some\":\"data\"}" "tags": ["key1:value1", "key1:value2"] } ``` ## EventId String based globally unique identifier of an [Event](#Event) * It _MUST_ satisfy the regular expression `^[\w\-]{1,100}$` * It _MAY_ be implemented as a [UUID](https://www.ietf.org/rfc/rfc4122.txt) ## EventType String based type of an event * It _MUST_ satisfy the regular expression `^[\w\.\:\-]{1,200}$` ## EventData String based, opaque payload of an [Event](#Event) * It _SHOULD_ have a reasonable large enough maximum length (depending on language and environment) * It _MAY_ contain [JSON](https://www.json.org/) * It _MAY_ be serialized into an empty string ## Tags A set of [Tag](#Tag) instances. * It _MUST_ contain at least one [Tag](#Tag) * It _MAY_ contain multiple [Tag](#Tag)s with the same value * It _SHOULD_ not contain muliple [Tag](#Tag)s with the same key/value pair ## Tag A `Tag` can add domain specific metadata (usually the ids of an entity or concept of the core domain) to an event allowing for custom partitioning **NOTE:** If the `value` is not specified, all tags of the given `key` will match (wildcard) * It _MUST_ contain a `key` that satisfies the regular expression `^[a-zA-Z0-9\-\_]{1,50}$` * It _CAN_ contain a `value` that satisfies the regular expression `^[a-zA-Z0-9\-\_]{1,50}$` ## AppendCondition * It _MUST_ contain a [StreamQuery](#StreamQuery) * It _MUST_ contain a [ExpectedHighestSequenceNumber](#ExpectedHighestSequenceNumber) ## ExpectedHighestSequenceNumber Can _either_ be an instance of [SequenceNumber](#SequenceNumber) Or one of: * `NONE` – No event must match the specified [StreamQuery](#StreamQuery) * `ANY` – Any event matches (= wildcard [AppendCondition](#AppendCondition))