This package aims to help you deal with data access when working with event sourced domain models.
dotnet add package NBB.Data.EventSourcing
It offers an EventSourcedRepository
that:
- reads/persists events from/into an
IEventStore
- manages snapshots using an
ISnapshotStore
- dispatches events using
MediatR
EventSourcedRepository
offers two operations needed when working with ES domains:
public interface IEventSourcedRepository<TAggregateRoot>
where TAggregateRoot : IEventSourcedAggregateRoot, new()
{
Task SaveAsync(TAggregateRoot aggregate, CancellationToken cancellationToken = default(CancellationToken));
Task<TAggregateRoot> GetByIdAsync(object id, CancellationToken cancellationToken = default(CancellationToken));
}
For EventSore and SnapshotStore configuration see NBB.EventStore
.
For more info about how to model event sourced snapshot-able entities see NBB.Domain
.
When loading domain aggregates, the repository uses the following algorithm:
- if the entity is snapshot-able it tries to load the last snapshot
- it loads the events from the event store (starting with last one from the snapshot, if any)
- it applies the events, re-hydrating the state of the aggregate, by calling the
LoadFromHistory
instance method on the ES aggregate root
When saving domain aggregates, the repository uses the following algorithm:
- fetches uncommitted events from the aggregate
- saves the events in the event store, with the concurrency control set to aggregate-loaded-at-version
- if the entity is snapshot-able it checks if it should persist a snapshot
- dispatches events using
MediatR
The process of mapping an event sourced entity to a stream, is a pure function like this:
public static string GetStream(this IIdentifiedEntity entity)
=> entity.GetType().FullName + ":" + entity.GetIdentityValue();
You can register custom options for ES data access like this:
services.AddEventSourcingDataAccess((sp, builder)=>builder.Options.DefaultSnapshotVersionFrequency = 5)
Register an ES repository for an aggregate like so:
services.AddEventSourcedRepository<Contract>();
For a working sample see: NBB.Contracts sample