Skip to content

Snapshots

Andrey Leskov edited this page Nov 22, 2016 · 7 revisions

Overview

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.

Implementation

Enabling snapshotting

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

Getting snapshots from aggregates

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 :

  1. Create additional private class with state in aggregate class, example

  2. Convert all aggregate state in separate class and use it both in aggregate operations and snapshot

  3. Use some external DTO class and map it to aggregate state

  4. 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.

Clone this wiki locally