Installation
composer require prooph/pdo-snapshot-store
Add the package to your config/app.php providers and aliases if not using auto-discovery.
Basic Configuration
Create a PDO connection (e.g., in config/filesystems.php or a custom config file):
'pdo_snapshot_store' => [
'connection' => 'mysql',
'table' => 'event_store_snapshots',
'schema' => 'public',
'snapshot_class' => \Prooph\SnapshotStore\PDO\Snapshot::class,
],
First Use Case: Storing a Snapshot
use Prooph\SnapshotStore\PDO\SnapshotStore;
use Prooph\SnapshotStore\PDO\Snapshot;
$snapshotStore = new SnapshotStore(
$pdoConnection,
new Snapshot('aggregate_type', 'aggregate_id', $snapshotData)
);
$snapshotStore->save($snapshot);
Retrieving a Snapshot
$snapshot = $snapshotStore->get('aggregate_type', 'aggregate_id');
Snapshot Strategy: Use snapshots to optimize event replay for large aggregates:
$aggregate = $repository->get($aggregateId, $snapshotStore);
The repository will automatically load the latest snapshot and replay only subsequent events.
Event Store Integration
Pair with prooph/event-store-pdo for a complete event-sourcing stack:
$eventStore = new EventStore(
$pdoConnection,
new EventStoreConfiguration(),
new SnapshotStore($pdoConnection)
);
Aggregate Hydration
$aggregate = $repository->get($aggregateId);
// Automatically loads snapshot + events
Snapshot Creation Trigger snapshots after critical events (e.g., every 100 events or on domain milestones):
$aggregate->markForSnapshot(); // Custom logic
$snapshotStore->save($aggregate->toSnapshot());
Cleanup Implement a cron job to purge old snapshots (e.g., older than 1 year):
$snapshotStore->deleteOlderThan(\DateTime::createFromFormat('Y-m-d', '2023-01-01'));
Use Laravel’s service container to bind the store:
$this->app->bind(SnapshotStore::class, function ($app) {
$pdo = $app['db']->connection('mysql')->getPdo();
return new SnapshotStore($pdo);
});
Schema Mismatches
Ensure your database table matches the expected schema (columns: aggregate_type, aggregate_id, snapshot_data, created_at). Run migrations:
Schema::create('event_store_snapshots', function (Blueprint $table) {
$table->string('aggregate_type');
$table->string('aggregate_id');
$table->json('snapshot_data');
$table->timestamp('created_at')->useCurrent();
$table->primary(['aggregate_type', 'aggregate_id']);
});
Snapshot Serialization
The snapshot_data column expects JSON-serializable data. Customize serialization if needed:
$snapshot = new Snapshot('type', 'id', json_encode($data));
Concurrency Issues Snapshots are not atomic by default. Use transactions for critical operations:
DB::transaction(function () use ($snapshotStore, $snapshot) {
$snapshotStore->save($snapshot);
});
aggregate_type and aggregate_id match exactly (case-sensitive).Schema::table('event_store_snapshots', function (Blueprint $table) {
$table->index(['aggregate_type', 'aggregate_id']);
});
Custom Snapshot Classes
Extend Prooph\SnapshotStore\PDO\Snapshot for domain-specific logic:
class UserSnapshot extends Snapshot {
public function __construct(string $aggregateId, array $data) {
parent::__construct('user', $aggregateId, $data);
}
}
Query Builder Override the default query logic for custom retrieval:
$snapshot = $snapshotStore->get('user', '123', new CustomQueryBuilder());
Event Listeners
Hook into snapshot events (e.g., SnapshotSaved) via Laravel’s event system:
event(new SnapshotSaved($snapshot));
prooph/test-helper to mock the snapshot store in unit tests:
$mockSnapshotStore = $this->prooph()->mockSnapshotStore();
version column to handle schema migrations gracefully.How can I help you explore Laravel packages today?