Embeddings

Overview of embeddings

Embeddings are like a combination of an Aggregate and a Projection. They have a collection of states (the read models) with unique keys (within the embedding). Whenever a new updated state is pushed/pulled from the external system, that updated state will be compared against its representative read model in the embedding. Whenever the states differ, the Runtime will call the embedding to resolve the difference into events. The embedding will then handle these events and modify its state to match the desired state.

The main point of embeddings is event source the changes coming from an external system. An embeddings read model exists so that we can commit the correct events and uphold its logic. Other event handlers, projections and microservices can then build upon these events.

Example of an embedding:

Diagram of embeddings

Read model

An embeddings read model functions like the state of an Aggregate Root. It upholds the invariants needed to produce the events to steer the read model towards the updated state. Whenever the read model is asked to update, it should resolve the difference between its own state and the updated state into events. These events are then projected into the read model, like a projection.

The read model instances are managed by the Runtime in the embedding store defined by resources.json.

Key

Each read model has its own unique key, which is defined in the update call to the embedding.

Unlike projections, embeddings don’t need KeySelectors. The Runtime will automatically calculate a unique EventSourceId for the committed events based on the key. This EventSourceId is then used to uniquely identify which read model should be handling the event.

Embedding events

The committed events are always public Aggregate events. The AggregateRootId is the same as the EmbeddingId, and the EventSourceId is computed from the read models key. This way each event can be uniquely identified to been originated by a specific read model and embedding.

You can’t “replay” an embeddings projection, and you shouldn’t need to. If you need the embedding to be in a specific state, you ask it to update itself to match the desired state. As each event of an embedding is specific to a key, embedding and the version of the embedding, you can’t “re-apply” those events to the embedding. If the read model and events need to change, you should create a new embedding to handle that.

Persistence

The embeddings definition is persisted in the embedding-definition collection in the database defined in resources.json. If this definition changes in some way (eg. new event types, different initial state) the registration will fail as the embedding isn’t the same embedding anymore.

The embeddings current AggregateRootVersion is saved to the aggregates collection.

Embeddings don’t produce streams.

Main structure of an Embedding

This is a simplified structure of an embedding:

Embedding {
    EmbeddingId Guid
    ReadModel type
    EventTypes EventType[]
}

For the whole structure of an embedding as defined in protobuf, please check Contracts.

Last modified January 1, 0001