Event Sourcing

Serialized provides a fully managed, scalable event store that you can use for Event Sourcing.

The Event Sourcing API is designed around Aggregates and Events. Both these concepts originates from Domain-Driven Design.

This page describes the different features and concepts in Serialized that provides the support for Event Sourcing. In summary these features are:

  • Simple event format and structure
  • Grouping of events within aggregates to provide a consistency boundary
  • An optimistic concurrency mechanism to enforce consistency when needed
  • Atomic writes and batching
  • Encryption
  • Event deletion

Event sourcing basics

An event describes something important that have happened in your business that should be stored as a fact. Events are always part of an aggregate, so you will always refer to an aggregate when working with events.

Event types

Each event is described by its type which should be written in the language of your domain.

Make sure to name your event type in past tense. Events should describe something that happened in the past in order to make sense. For example, use order-placed as opposed to order-placement or place-order.

Event data

The event data is the additional data that should be stored with the event. For example, an order-registered event could contain data fields for orderAmount and customerId.

The data that you store can be used during materialization of the aggregate to implement business rules and invariants that should hold true within your domain.

It is optional to provide additional data with the event. Since events have a type that provide meaning to the application purely by their name, it is sometimes enough to store an event without additional data.

Uniqueness and identification of events

Your events have a unique identifier that is of the format of UUID. You can supply this id in the API or you can omit the id and let Serialized generate a random unique id when the Event is stored.

Immutability

Since Events are immutable by definition, you cannot change an event that has been stored. To undo a change you should instead store an event that is a reversal/compensation of the event that you want to change and add handling of that event to downstream services such as Projections and Reactions.

Aggregates

Aggregates are ordered sequences of batches of Events. It is your system and application code that will implement the business rules of the Aggregate. Serialized will take care of storing all Events for the Aggregate efficiently and providing the APIs you need to manage the lifecycle of the Aggregate.

Aggregate versioning

All aggregates have a current version. The version is a monotonically increasing number that will be increased by one for every event batch that is successfully stored for that aggregate. This version is used to enforce consistency when using our optimistic concurrency mechanism (see section below).

Atomic writes

Sometimes multiple events are a consequence of a single command. For example, a game round in a card game might finish as a consequence of the last card being played by the last player.

In these situations it is important that all events are stored together to avoid logical data corruption in the Aggregate. This is why all events are stored as batches of events in Serialized.

Optimistic locking

The event store is the source of truth for an event sourced system, and we trust it to represent the consistent event history of the aggregates. When having multiple writers to an aggregate, there is a possibility that multiple writers will try to update the same aggregate at the same time.

In the last step of this picture, Client 2 have outdated knowledge about the aggregate, since Client 1 have updated the aggregate after it was last read by Client 2. If Client 2 would be allowed to store events at this point, the event history of the aggregate might be corrupted.

To resolve this, Serialized provide a simple yet powerful optimistic concurrency mechanism that gives you the power to either reject or retry any operation when a conflicting update is detected. Our APIs and client libraries will return a conflict error if the client provides an expectedVersion as part of the store request and that version does not match the current aggregate version.

If a conflict occurs, you can retry the operation or return an error to the client, depending on the situation.

Encryption

Events may also contain a special field for storing data that has been client-side encrypted before stored. This field is never used in our internal projections and is never parsed in any way. It is treated as a blob by our services.

Deleting events

In the case you accidentally stored secret data or have to clean your data by request from a customer Serialized provide the support to delete events. You can delete events by deleting the aggregate that contains the event.

More information

To read more about the details of our event sourcing API, see our API documentation.