In sec some basic data types surface in many use cases when interacting with EventStoreDB. In order to establish some basics these are described in the following - More details about the particulars of these types are in API documentation.
Streams in EventStoreDB have stream identifiers that can be classified as user defined and system defined.
In EventStoreDB streams prefixed with
$ are reserved for the system, for instance
Furthermore, EventStoreDB also has a concept of metadata streams for streams. Metadata streams for system streams are prefixed
$$, e.g. the corresponding metadata stream for
$$$settings. Metadata streams for user defined
streams are prefixed with a
user_stream has a corresponding metadata stream named
In sec a stream identifier is an ADT called
StreamId with variants
has two variants,
System. The stream identifiers that a user can create are
this is done with
StreamId.apply that returns an
Either[InvalidInput, Id]. Some examples of
Moreover, a few common system stream identififiers are located in the
StreamId companion object, for instance:
When you store an event in EventStoreDB it is assigned a stream position in the individual stream it belongs to.
In sec a stream position is an ADT called
StreamPosition with two variants,
StreamPosition.Exact that contains
StreamPosition.End that is an object representing the end of a stream.
StreamPosition that you can create is
Exact and this is done with
StreamPosition.apply that returns an
Exact. Examples of
One use case where you need to construct a
StreamPosition is when you to store a pointer of the last processed
event for a particular stream as a
Long in a read model and, e.g. after a restart of your application,
need to resume reading from EventStoreDB.
All events in EventStoreDB have a logical position in the global transaction log. A logical position consists of a commit position value
and a prepare position value. In sec this is modelled as an ADT called
LogPosition with two variants,
that contains two
Long values and
LogPosition.End representing the end of the log.
LogPosition that you can create from
Long values is
Exact, this is done with
LogPosition.apply that returns an
Either[InvalidInput, Exact]. Examples of
Cases where you construct a
LogPosition is similar to that of
StreamPosition, maintaining a pointer of last
processed event. However, here you keep a pointer to the global log,
StreamId.All, instead of an individual
A note on
Long usage in positions#
uint64 that Java does not have a corresponding type for. In order to work around that fact sec has a
ULong that is able to represent
uint64 by wrapping a regular
Long and use this for
LogPosition.Exact. As sec provides an instance of
ULong it is possible to compare positions larger than
Long.MaxValue as well as positions in
Moreover, this means that you can store a
LogPosition.Exact pointer for your read model by using
toLong on its
ULong values. The
ULong.toLong method might yield negative values, this is fine as when
LogPosition.Exact is constructed again it has an
cats.Order instance that works for all numbers in
Some operations, such as appending events to a stream, require that you provide an expectation of what state
the stream currently is in. If the stream state does not fullfil that expectation then an exception will be raised by EventStoreDB.
In sec this expected stream state is represented by the ADT
StreamState that has four variants:
NoStream- The stream does not exist yet.
Any- No expectation about the current state of the stream.
StreamExists- The stream, or its metadata stream, is present.
StreamPosition.Exact- The stream exists and its last written stream position is expected to be
StreamState expectation can be used to implement optimistic concurrency. When you retrieve a stream from EventStoreDB,
you can take note of the current stream position, then when you append to the stream you can determine if the stream has
been modified in the meantime.
The event data you store in EventStoreDB is composed of an event type, an event id, payload data, metadata and a content type.
In sec this is modelled as
EventData with types that are explained below.
An event type should be supplied for your event data. This is a unique string used to identify the type of event you are saving. One might be tempted to use language runtime types for event types as it might make marshalling more convenient. However, this is not recommended as it couples storage to your types. Instead, you can use a mapping between event types stored in EventStoreDB and your concrete runtime types.
The string that EventStoreDB uses for the event type is modelled in sec as an ADT with two main variants
System. The type that you can create is
Normal and this is done with
Either[InvalidInput, Normal]. Input is validated for emptiness and not starting with
$ that EventStoreDB
uses for reserved system defined types such as
Common system types are located in the companion of
EventType, some examples are:
The format of an event identifier is a uuid and is used by EventStoreDB to uniquely identify the event you are trying to append. If two events with the same uuid are appended to the same stream in quick succession EventStoreDB only appends one copy of the event to the stream. More information about this is in the EventStoreDB docs about concurrency and idempotence.
data field on
EventData is a scodec
ByteVector encoded representation
of your event data. If you store your data as JSON you can make use of EventStoreDB functionality for projections.
However, it is also common to store data in a
protocol buffers format.
It is common to store additional information along side your event data. This can be correlation id, timestamp, audit,
marshalling info and so on. EventStoreDB allows you to store a separate byte array containing this information to keep data
and metadata separate. These extra bytes are stored in a
ByteVector field of
metadata fields on
EventData have a content type,
Json. This is used to provide EventStoreDB information about whether the data is stored as json or as
binary. As you might have noticed, both
ByteVector fields of
EventData share the same content type, this is because
EventStoreDB does not support different content types for
EventType is translated to the protocol of EventStoreDB it becomes
Json. In the future there might come more content types, e.g. something that corresponds to
Event data arriving from EventStoreDB either comes from an individual stream or from the global log. Data retrieved
from the global log contains positions
StreamPosition.Exact, whereas data retrieved
from an individual stream contains only
The difference in position information is encoded in the ADT that models an event,
Event[P <: PositionInfo] where
PositionInfo.Global containing both position types or
PositionInfo.Local that is an alias for
Event has the variants
EventRecord consists of the following data types:
streamId: StreamId- The stream the event belongs to.
position: P- The
Pabout the event.
eventData: EventData- The data of the event.
created: ZonedDateTime- The time the event was created.
ResolvedEvent is used when consuming streams that link to other streams. It consists of:
event: EventRecord- The linked event.
link: EventRecord- The linking event record.
See the API docs for various methods defined for
Later on when using the EsClient API, you will learn about reading from streams and instruct EventStoreDB to
resolve links such that you get events of type