A stream is a sequence of events, organized by revision. Streams are utilized in various domains. Here are a few examples of event streams:
- The state changes of a domain aggregate
- A user's financial transactions
- A series of telemetry readings from an IoT device
- And more.
An event is a fact. The domain may be updated to reflect the fact represented by the event.
Each event has a unique identifier, a timestamp, and a payload.
A stream identifier is a unique identifier of the stream.
It is an abstraction of the sequence of events, guaranteeing uniqueness and order, as well isolation.
Example of stream IDs:
- Identifier of domain aggregate root.
- Identifier of user.
- Identifier of IoT device.
- etc.
Event identifier is a unique identifier of the event. Guarantee uniqueness of the event in the stream.
Revision is a sequence number of the event in the stream. It is used to:
- Order events in the stream.
- Establish concurrency control.
Despite the fact that event timestamp can be used to order events, it is not really reliable, since there could be a clock skew between different part of the system in distributed architecture.
Collection of additional data you can store and retrieve for event. Can be used to store event metadata specific for particular solution.
Storage is a physical implementation of the event stream i.e. persistence layer. It is responsible for storing and retrieving events from the stream.
-
Idis a value object (immutable) that has implicit conversion from and to string.Thus you don't need to create Id object explicitly and use
ToString()to convert to string back.
Also implementsIEquatablefor itself and forString. -
Revisionis a value object (immutable) that represents a revision of the stream.
It is used for optimistic concurrency control and event ordering. It has implicit conversion from and toInt32type.
Also implementsIEquatableandIComparablefor itself and forInt32. -
You can read from any stream starting from the provided revision.
-
ReadToEndmethod returns collection of events from the stream starting from the provided revision:- Contains only unique events ordered by revision.
- Contains only events that were committed.
-
Stream revision is always the revision of an event with maximum revision value.
-
Idempotency of reading and deletion fully depends on particular storage implementation.
-
You don't need to retrieve stream to add events to it. Appending to stream and getting stream are separate operations.
-
Despite the fact that reading is declared as asynchronous and iterative operation, for the sake of performance it is implemented as paginated operation.
You can define the page size by using
WithReadingPageSizemethod of store configuration, by default it is 10 events. -
Reading and writing operations are not thread-safe.
Thus, it is not recommended to use the same instances ofIStreamUnitOfWorkorIAsyncEnumerable<StreamEvent>in multiple threads.