Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Snapshot Store Laravel Package

prooph/snapshot-store

Lightweight snapshot store for prooph/event-sourcing setups. Provides a simple API to persist and load aggregate snapshots, reducing replay time and improving performance. Note: library support ended Dec 31, 2019 (deprecated).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install Dependencies:

    composer require prooph/snapshot-store prooph/event-sourcing prooph/snapshotter
    

    Note: Requires prooph/event-sourcing and prooph/snapshotter for full functionality.

  2. Configure a Basic PDO Store:

    use Prooph\SnapshotStore\Pdo\PdoSnapshotStore;
    use Prooph\SnapshotStore\Serializer\CallbackSerializer;
    
    $pdo = new \PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
    $serializer = new CallbackSerializer('serialize', 'unserialize');
    
    $snapshotStore = new PdoSnapshotStore(
        $pdo,
        ['snapshot_table' => 'aggregate_snapshots'],
        'default_snapshot_table',
        $serializer
    );
    
  3. Integrate with Aggregate Repository:

    use Prooph\EventSourcing\Aggregate\AggregateRepository;
    
    $repository = new AggregateRepository(
        $metadata,
        $snapshotStore,
        $eventStore
    );
    
  4. First Use Case: Load an aggregate with snapshots enabled:

    $aggregate = $repository->get($aggregateId, $aggregateType);
    

    Snapshots will now hydrate the aggregate, bypassing event replay when available.


Implementation Patterns

Usage Patterns

  1. Snapshot-Based Hydration:

    • Configure AggregateRepository to use snapshots:
      $repository = new AggregateRepository(
          $metadata,
          $snapshotStore,
          $eventStore,
          new Snapshotter($snapshotStore) // Optional: Auto-snapshot on save
      );
      
    • Snapshots are stored after events are applied (use Snapshotter for automation).
  2. Composite Store for Caching:

    • Combine in-memory (Redis) and persistent (PDO) stores:
      use Prooph\SnapshotStore\CompositeSnapshotStore;
      
      $cacheStore = new RedisSnapshotStore($redis);
      $persistenceStore = new PdoSnapshotStore($pdo);
      
      $compositeStore = new CompositeSnapshotStore([$cacheStore, $persistenceStore]);
      
    • Order matters: Cache layers should be first for performance.
  3. Custom Serialization:

    • Optimize for speed (e.g., igbinary) or compatibility (e.g., JSON):
      $serializer = new CallbackSerializer('igbinary_serialize', 'igbinary_unserialize');
      $snapshotStore = new PdoSnapshotStore($pdo, [], 'snapshots', $serializer);
      
  4. Bulk Operations:

    • Clear snapshots by aggregate type:
      $snapshotStore->deleteAllByAggregateType('Order');
      

Workflows

  1. Event-Sourcing Workflow:

    • Save: Events → Snapshots (auto via Snapshotter).
    • Load: Snapshots → Replay missing events.
    • Fallback: Full replay if snapshots are missing/corrupt.
  2. Read-Heavy Optimizations:

    • Use CompositeSnapshotStore to cache hot aggregates (e.g., UserProfile).
    • Monitor snapshot hit ratios to justify storage overhead.
  3. Testing:

    • Mock SnapshotStore in unit tests:
      $mockStore = $this->createMock(SnapshotStore::class);
      $mockStore->method('load')->willReturn($snapshotData);
      

Integration Tips

  • Laravel: Register the store in a service provider:
    $this->app->singleton(SnapshotStore::class, function ($app) {
        return new PdoSnapshotStore($app['db.connection'], [], 'snapshots');
    });
    
  • Prooph Snapshotter: Auto-snapshot aggregates on save:
    $repository = new AggregateRepository(
        $metadata,
        $snapshotStore,
        $eventStore,
        new Snapshotter($snapshotStore, 10) // Snapshot every 10 events
    );
    
  • Database Schema: Create a snapshot table:
    CREATE TABLE aggregate_snapshots (
        aggregate_id VARCHAR(36) PRIMARY KEY,
        aggregate_type VARCHAR(255),
        snapshot_data LONGTEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    

Gotchas and Tips

Pitfalls

  1. Deprecation Risk:

    • No active development since 2021. Plan for migration to alternatives (e.g., EventStoreDB, Axon Framework).
    • Mitigation: Fork the package if critical bugs arise.
  2. Snapshot Corruption:

    • If snapshots are corrupted or deserialization fails, the system falls back to event replay.
    • Tip: Validate snapshots during load:
      try {
          $snapshot = $snapshotStore->load($aggregateId, $aggregateType);
          // Deserialize and verify aggregate state.
      } catch (\Exception $e) {
          // Fallback to event replay.
      }
      
  3. Performance Trade-offs:

    • Large snapshots increase storage costs and serialization overhead.
    • Tip: Use igbinary for smaller snapshots or compress data (e.g., gzip).
  4. Composite Store Order:

    • Incorrect order in CompositeSnapshotStore may bypass persistence layers.
    • Tip: Always place persistent stores last:
      $compositeStore = new CompositeSnapshotStore([$cacheStore, $persistenceStore]);
      
  5. Concurrency Issues:

    • Concurrent writes to the same snapshot may cause race conditions.
    • Tip: Use database transactions or optimistic locking.

Debugging

  1. Missing Snapshots:

    • Verify the snapshot table exists and has write permissions.
    • Check if Snapshotter is configured in the repository.
  2. Deserialization Errors:

    • Ensure the serializer matches the stored data (e.g., igbinary vs. serialize).
    • Debug: Log raw snapshot data before deserialization.
  3. Slow Loads:

    • Profile snapshot load times vs. event replay.
    • Tip: Use Xdebug to compare performance:
      $start = microtime(true);
      $aggregate = $repository->get($id, $type);
      echo "Snapshot load time: " . (microtime(true) - $start) . "s";
      

Config Quirks

  1. Table Naming:

    • The PdoSnapshotStore uses snapshot_table config. Defaults to aggregate_snapshots if omitted.
    • Example:
      $snapshotStore = new PdoSnapshotStore($pdo, ['snapshot_table' => 'my_snapshots']);
      
  2. Aggregate Type Handling:

    • Snapshots are scoped by aggregate_type. Ensure consistency in type names across stores.
  3. Serializer Compatibility:

    • Custom serializers must implement Prooph\SnapshotStore\Serializer\SerializerInterface.
    • Example:
      class JsonSerializer implements SerializerInterface {
          public function serialize($data) { return json_encode($data); }
          public function unserialize($data) { return json_decode($data, true); }
      }
      

Extension Points

  1. Custom Store Backends:

    • Implement Prooph\SnapshotStore\SnapshotStoreInterface for MongoDB, DynamoDB, etc.
    • Example:
      class MongoSnapshotStore implements SnapshotStoreInterface {
          public function load($aggregateId, $aggregateType) { /* ... */ }
          public function save($aggregateId, $aggregateType, $snapshot) { /* ... */ }
          public function delete($aggregateId, $aggregateType) { /* ... */ }
          public function deleteAllByAggregateType($aggregateType) { /* ... */ }
      }
      
  2. Snapshot Validation:

    • Extend Snapshotter to validate snapshots before saving:
      $snapshotter = new Snapshotter($snapshotStore, 10, function ($snapshot) {
          return strlen($snapshot) < 1024 * 1024; // Reject large snapshots.
      });
      
  3. Event-Based Snapshotting:

    • Trigger snapshots on specific events (e.g., OrderPaid):
      $repository = new AggregateRepository(
          $metadata,
          $snapshotStore,
          $eventStore,
          new EventBasedSnapshotter($snapshotStore, ['OrderPaid'])
      );
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky