Install from NuGet:
Install-Package Orleans.EventSourcing.EventStorage
or
dotnet add package Orleans.EventSourcing.EventStorage
Configure your silo:
builder.Host.UseOrleans(siloBuilder =>
{
siloBuilder.UseLocalhostClustering();
siloBuilder.AddEventStorageBasedLogConsistencyProviderAsDefault();
siloBuilder.AddMemoryEventStorageAsDefault();
});
Orleans is a cross-platform framework for building robust, scalable distributed applications.
Event sourcing is approach to handling operations on data that's driven by a sequence of events, each of which is recorded in an append-only store.
Read more:
- Azure Architecture Center - Event Sourcing Pattern
- Martin Fowler - Event Sourcing
- Microservices.io - Pattern: Event sourcing
Not only does the Orleans framework make it extremely easy to implement the event sourcing pattern, but they also compliment each other really well. Orleans can solve many of the issues that come along with using event sourcing, and vice versa.
Orleans is not great at doing queries over grain data. Event sourcing can help resolve this shortcoming by using it alongside CQRS. The combination of event sourcing and CQRS means that you can implement a write side (commands) using orleans grains, while also having a read side (queries) that are simply a projection of the events in the event store.
Orleans also struggles with certain aspects of event driven architectures, such as reliably publishing events if and only if a write is successful. In many traditional architectures this is achieved using a transactional outbox, which is unfortunately quite complex to implement in Orleans due to the way grain data is persisted by default. Using event sourcing with your Orleans grains resolves this issue since the state being stored and the event to be published are one and the same.
Orleans can also help with some of the shortcomings of traditional event sourcing. One of the common struggles with event sourcing is the need to rehydrate the current state of an entity from the events stored in the event store. If the stream of events is long, this can become a costly operation. Orleans helps in this regard because it can keep grains alive and incrementally apply updates to the activated grains, reducing the number of times that the entire event stream needs to be read and applied.
Orleans currently ships with 3 built-in log-consistency providers.
Stores grain snapshots, which is to say that it stores only the most recent version of the grain state in storage. In many ways this is not true event sourcing because it does not actually persist the log of events.
Stores the complete event sequence as a single object. While this does store all the events in the log, it is not recommended for production use because it stores the entire sequence in a single entry that must be read/written every time the entry is accessed.
Custom storage is flexible provider that allows the developer to plug-in their own storage mechanism while assuming very little about how the events will be stored. While this can be an effective option, it has its downsides:
- It requires implementing the ICustomStorageInterface<TState,TDelta> interface in your grains. This means your grains become responsible for managing their own storage, which is a violation of the single-responsibility principle and separation of concerns.
- A different implementation of ICustomStorageInterface<TState,TDelta> is required for every grain type that needs to be stored.
- It's a heavy-handed approach that isn't necessary for most people for whom a more pre-configured solution would suffice.
- The custom storage provider doesn't support
RetrieveConfirmedEvents
.