This PR introduces support for snapshotting as an opt-in feature. In order to start using snapshotting you'll need to the following:
Step 1
Make sure your aggregate root implements the EventSauce\EventSourcing\Snapshotting\AggregateRootWithSnapshotting
interface. This interface extends the base interface so you only need one.
Step 2
Use the EventSauce\EventSouring\Snapshotting\SnapshottingBehaviour
trait in addition to the AggregateRootBehaviour
trait. This will require you to implement the protected function createSnapshotState();
and abstract protected function setPayloadState($state): void;
methods.
The goal of these methods is to facilitate storing of the aggregate's state at any given point in time.
Step 3
Create an implementation of the EventSauce\EventSourcing\Snapshotting\SnapshotRepository
interface. You can use your preferred way of storing your snapshot in any persistence you like.
Step 4
Start using the \EventSauce\EventSourcing\Snapshotting\AggregateRootRepositoryWithSnapshotting
interface, a concrete implementation is provided: \EventSauce\EventSourcing\Snapshotting\ConstructingAggregateRootRepositoryWithSnapshotting
This implementation decorated the existing AggregateRootRepository
interface.
Usage
An example aggregate root with snapshotting support:
<?php
namespace EventSauce\EventSourcing\Snapshotting\Tests;
use EventSauce\EventSourcing\AggregateRootBehaviour;
use EventSauce\EventSourcing\Snapshotting\SnapshottingBehaviour;
use EventSauce\EventSourcing\Snapshotting\AggregateRootWithSnapshotting;
class LightSwitch implements AggregateRootWithSnapshotting
{
use AggregateRootBehaviour, SnapshottingBehaviour;
const OFF = false;
const ON = true;
private $state = self::OFF;
private function createSnapshotState()
{
return $this->state;
}
private function setPayloadState($state)
{
$this->state = $state;
}
public function turnOn(): void
{
if ($this->state == self::OFF) {
$this->recordThat(LightSwitchWasFlipped::on());
}
}
public function turnOff(): void
{
if ($this->state == self::ON) {
$this->recordThat(LightSwitchWasFlipped::off());
}
}
protected function applyLightSwitchWasFlipped(LightSwitchWasFlipped $event)
{
$this->state = $event->state();
}
}
Using the new aggregate root repository can be done as followed:
$aggregateRoot = $aggregateRootRepository->retrieveFromSnapshot($aggregateRootId);
// interact with the model
// persist to store and dispatch events as usual
$aggregateRootRepository->persist($aggregateRoot);
// update the snapshot
$aggregateRootRepository->storeSnapshot($aggregateRoot);
Behaviour Explained
The snapshotting mechanism stores a snapshot state along with the aggregate root id and version. When reconstituting from a snapshot, the snapshot is retrieved from storage, and any events that happened after the snapshot was stored are fetch and applied to the model. This prevents you from needing to store every version. It still gives you the latest state of your aggregate root, even when events have been recorded after storing the snapshot.