-
Notifications
You must be signed in to change notification settings - Fork 7
Snapshots
Snapshot is a record of event sourced entity state. It is used for performance boost to avoid hydrating state from event stream each time it is loaded, as stream can be really huge (imaging 10.000+ events).
Grid domain has only one event-sourced entity - aggregate, and it is used byself and by sagas, so behavior is similar.
Snapshot can be saved after state modification, according to decision from special policy. For aggregates, snapshots can be saved once per command or less, based on domain events produced amount. For sagas, snapshots can be saved once per transition or less, based on domain events produced amount.
Old snapshots can be deleted after aggregate or saga is unloaded from memory due to inactivity. Decision is made by policy and is based on events number produced.
During aggregate or saga load, latest snapshot is used to retrieve state.
GridDomain supports snapshots for aggregates, instance and states sagas. By default snapshotting is disabled.
To enable it add SnapshotsSavePolicy parameter to according ContainerConfiguration.
new AggregateConfiguration<SampleAggregate, SampleAggregatesCommandHandler>(
new SnapshotsSaveAfterEachMessagePolicy())
If you are using container extensions RegisterAggregate, consider switching to explicit call to AggregateConfiguration
c.RegisterAggregate<BalanceAggregate,BalanceAggregatesCommandHandler>()
=>
c.Register(new AggregateConfiguration<SampleAggregate, SampleAggregatesCommandHandler>(
new SnapshotsSaveAfterEachMessagePolicy()))
Snapshots can be saved by each several message and by time passed from last activity.
Messages are counted only within actor lifetime, and not persisted between grid node restarts.
To configure you own parameters, use SnapshotsSavePolicy class.
GridDomain comes with two predefined snapshots save policies:
NoSnapshotsSavePolicy - default one, to disable snapshotting
EachMessageSnapshotsSavePolicy - will save snapshot after each message
You can find difference examples of snapshots behavior here
GridDomain has only one interface to create snapshots - it is IConstuctAggregates. By default AggregateFactory implementation is used.
To save snapshot we need an object implementing IMemento:
public interface IMemento
{
Guid Id { get; set; }
int Version { get; set; }
}
This object will be serialized on snapshot persist in database and deserialized on snapshot load.
Make sure all objects
implementing IMemento can be deserialized with all information remaining.
You can test it by implementing you test inherited from GridDomain.Tests.Framework.TypesDeserializationTest.
Aggregate base class implements IMemento itself and is used by default if snapshotting is enabled.
To provide different implementation of IMemento you need to override Aggregate.GetSnapshot() method and
pass custom factory creating your aggregate from snapshot.
You are free in decision how to implement custom snapshot, here are some common options :
-
Create additional private class with state in aggregate class, example
-
Convert all aggregate state in separate class and use it both in aggregate operations and snapshot
-
Use some external DTO class and map it to aggregate state
-
Save aggregate as is without special class for
IMemento
To use your custom Factory for creating aggregate from snapshots, we need to implement custom IConstructAggregate
and pass it as parameter to AggregateConfiguration or 'SagaConfiguration', as here
There is an overload for configurations allowing to pass only Func<IMemento, IAggregate> instead of IConstructAggregates. It can be used for simplification, for example when aggregate itself has a static factory method,
as in example
Using your own implementation of IConstructAggregates allows to resolve parameters from DI for aggregate constructor.