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

Pdo Event Store Laravel Package

prooph/pdo-event-store

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Event Sourcing Pattern Alignment: The GapDetector integration in PdoEventStoreReadModelProjector remains a critical enhancement for ES/CQRS architectures, ensuring projection reliability by detecting and handling missing events during replay. This is particularly valuable for eventual consistency in distributed systems, where partial event histories are a risk.
  • DDD Compatibility: Strengthens materialized view resilience, aligning with Prooph’s event-driven DDD principles. The GapDetector enables self-healing projections, reducing the need for manual intervention during event replay.
  • Hybrid System Suitability: Continues to support polyglot persistence while improving fault tolerance for projections, especially in mixed legacy/modern event-driven systems.

Integration Feasibility

  • PDO Abstraction: Unchanged—remains database-agnostic (MySQL, PostgreSQL, etc.). No breaking changes to PDO dependency.
  • Prooph Ecosystem Synergy: The GapDetector is now natively integrated into PdoEventStoreReadModelProjector, reducing projection failures when used with Prooph’s Service Bus or Snapshotter. Teams already using Prooph’s projection tools will see minimal adaptation overhead.
  • Event Serialization: No changes to the serialization layer. Custom serializers (JSON, MsgPack, etc.) remain pluggable, but ensure they include event metadata (e.g., event_id, stream_name) required for gap detection.

Technical Risk

  • Schema Management: No risk introduced. The GapDetector operates at the projection layer, not the event store schema.
  • Performance at Scale:
    • Gap detection adds overhead during projection replay. Benchmark large-scale replays to assess impact on throughput, especially for high-volume streams.
    • Hotspots: Still a risk for single-table storage; partitioning/sharding remains necessary for horizontal scaling.
  • Concurrency Control: Unchanged. Optimistic locking (event_version) is unaffected.
  • Event Size Limits: No changes. Large payloads may still require compression/out-of-band storage.
  • Legacy System Gaps: Mitigated by GapDetector: Projections are now more resilient to partial event history, easing migration from non-event-driven systems.

Key Questions

  1. Projection Reliability: How will the team handle detected gaps (e.g., retries, compensating transactions, or manual resolution)?
  2. Performance Impact: Will GapDetector overhead degrade replay speed for high-volume streams? Test with load simulations and real-world event distributions.
  3. Monitoring: How will gap events be logged/monitored? (e.g., Laravel’s Log::error, custom metrics, or observability tools like Datadog).
  4. Backfill Strategy: If migrating legacy data, how will gap detection affect initial projection sync? Will gaps trigger automatic recovery or require manual intervention?
  5. Fallback Mechanisms: Are there circuit breakers or dead-letter queues for projection failures during critical operations?
  6. Database Indexing: Ensure stream_name, event_id, and event_version are indexed for optimal gap detection performance.
  7. Event Store Versioning: Verify compatibility with existing event schemas—no breaking changes expected, but validate serialization includes required metadata.

Integration Approach

Stack Fit

  • PHP/Laravel Ecosystem: Seamlessly integrates with Laravel + Prooph. The GapDetector enhances read model projections, which can be implemented as:
    • Laravel Queues: Async projection jobs with retry logic for gaps (e.g., using prooph/service-bus).
    • Laravel Listeners: Sync projections with gap handling middleware (e.g., HandleGapException).
  • Prooph Dependency: Critical for full functionality. Teams not using Prooph must adopt its projection interfaces (ReadModelProjector) to leverage GapDetector. Alternatives like EventSauce lack Laravel integration.
  • Database Layer: PostgreSQL recommended for JSONB support and advanced indexing. MySQL/SQLite may require custom gap detection logic (e.g., manual WHERE event_id NOT IN (...) queries).
  • Alternatives Considered:
    • Custom Projections: For non-Prooph setups, implement manual gap checks (e.g., WHERE event_id > last_processed_id).
    • EventSauce: Offers built-in gap detection but lacks Prooph’s Laravel integration.

Migration Path

  1. Phase 1: Proof of Concept
    • Test GapDetector with a sample projection to validate gap handling.
    • Simulate missing events (e.g., manually delete events from the store) and verify projection recovery.
    • Example:
      $eventStore->deleteEventsFromStream('stream-1', ['event-id-2']);
      $projector->project($streamName, $handler);
      $this->assertEquals(['event-1', 'event-3'], $readModel->getEvents());
      
  2. Phase 2: Core Integration
    • Replace naive projections with PdoEventStoreReadModelProjector + GapDetector.
    • Integrate with Prooph Service Bus for command-driven projections.
    • Use Laravel’s queue system to parallelize projection jobs and isolate gap failures.
  3. Phase 3: Scaling & Optimization
    • Optimize gap detection: Index event_id and stream_name for faster gap queries.
    • Batch processing: Process events in chunks to reduce memory overhead during replay.
    • Monitor gaps: Log detected gaps to Laravel Sentry or Datadog for alerting.
  4. Phase 4: Legacy System Sync
    • Backfill with gap awareness: Use GapDetector to skip known gaps during initial sync.
    • Dual-write phase: Temporarily run old and new projections in parallel to validate consistency.

Compatibility

  • Laravel Service Container: Bind ReadModelProjector with GapDetector:
    $app->bind(ReadModelProjector::class, function ($app) {
        return new PdoEventStoreReadModelProjector(
            $app->make(EventStore::class),
            new GapDetector(), // Native integration in v1.16.5
            $app->make(EventSerializer::class)
        );
    });
    
  • Event Serialization: Unchanged. Ensure serialized events include all metadata required for gap detection (e.g., event_id, stream_name).
  • Transaction Support: GapDetector operates outside transactions by design. Use Laravel’s queue retries for idempotent gap handling.
  • Testing: Extend tests to verify gap scenarios:
    // Example: Simulate a gap and assert projection recovery
    $eventStore->deleteEventsFromStream('stream-1', ['event-id-2']);
    $projector->project($streamName, $handler);
    $this->assertEquals(['event-1', 'event-3'], $readModel->getEvents());
    

Sequencing

  1. Database Setup: Add indexes for gap detection (if not present):
    CREATE INDEX idx_event_store_stream_event_id ON event_store(stream_name, event_id);
    
  2. Prooph Initialization: Configure ReadModelProjector with GapDetector:
    $projector = new PdoEventStoreReadModelProjector(
        $eventStore,
        new GapDetector(), // Native in v1.16.5
        $serializer
    );
    
  3. Projection Handler: Implement ReadModelProjectorHandler with gap-aware logic:
    $projector->project('order-stream', new OrderProjectionHandler());
    
  4. Gap Handling Strategy: Define retry policies (e.g., exponential backoff) for failed projections.
  5. Monitoring: Instrument GapDetector to log gaps to Laravel’s monitoring tools:
    $gapDetector->onGapDetected(function (Gap $gap) {
        Log::warning("Gap detected in stream {$gap->getStreamName()}: missing event {$gap->getExpectedEventId()}");
    });
    

Operational Impact

Maintenance

  • Schema Evolution: No changes required. Gap detection operates at the application layer.
  • Dependency Updates: Minor update (v1.16.5). Verify compatibility with:
    • Prooph Service Bus (prooph/service-bus:^2.0).
    • Laravel’s queue system (if using async projections).
  • Logging: Extend logging to capture gap events:
    $gapDetector->onGapDetected(function (Gap $gap) {
        Log::warning("Gap detected in stream {$gap->getStreamName()}: missing event {$gap->getExpectedEventId()}");
    });
    
  • Backups: Critical for gap recovery. Ensure backups include **full event history
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.
anousss007/vigilance
supportpal/eloquent-model
ardenexal/fhir-models
laravel-at/laravel-image-sanitize
romalytar/yammi-audit-log-laravel
ardenexal/fhir-validation
arshaviras/weather-widget
laravel-chronicle/core
sunchayn/nimbus
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon