Weave 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

Event Sourcing Laravel Package

dddominio/event-sourcing

Laravel package for event sourcing in DDD-style apps. Store and replay domain events to rebuild aggregates, keep an append-only event log, and track state changes over time. Useful for audit trails, projections, and CQRS-inspired architectures.

View on GitHub
Deep Wiki
Context7

Getting Started

First Steps

  1. Installation

    composer require dddominio/event-sourcing
    

    Ensure your project uses PHP 8.0+ and Laravel 8+ (or Lumen).

  2. Basic Setup

    • Publish the config (if needed):
      php artisan vendor:publish --provider="Dddominio\EventSourcing\EventSourcingServiceProvider"
      
    • Register the package in config/app.php under providers (if not auto-discovered).
  3. Define an Aggregate Root Create a class extending Dddominio\EventSourcing\AggregateRoot:

    namespace App\Domain\Posts;
    
    use Dddominio\EventSourcing\AggregateRoot;
    
    class Post extends AggregateRoot
    {
        public function create(string $title, string $content)
        {
            $this->recordThat(new PostCreated($title, $content));
        }
    }
    
  4. Define Events Create a class implementing Dddominio\EventSourcing\DomainEvent:

    namespace App\Domain\Posts\Events;
    
    use Dddominio\EventSourcing\DomainEvent;
    
    class PostCreated implements DomainEvent
    {
        public function __construct(
            public string $title,
            public string $content
        ) {}
    }
    
  5. First Use Case: Publishing an Event

    $post = new Post();
    $post->create("Hello World", "First post!");
    $post->publish(); // Persists events to storage
    

Implementation Patterns

Workflow: Event-Driven Domain Logic

  1. Command Handling Use a service layer to trigger domain logic:

    namespace App\Services;
    
    use App\Domain\Posts\Post;
    use App\Domain\Posts\Events\PostCreated;
    
    class PostService
    {
        public function createPost(string $title, string $content)
        {
            $post = new Post();
            $post->create($title, $content);
            $post->publish();
        }
    }
    
  2. Event Sourcing Storage Configure storage (default: database) in config/event-sourcing.php:

    'storage' => [
        'driver' => 'database',
        'table' => 'event_store',
    ],
    

    For custom storage (e.g., Redis), implement Dddominio\EventSourcing\Storage\EventStorageInterface.

  3. Event Projection (Read Models) Subscribe to events to update read models:

    namespace App\Listeners;
    
    use App\Domain\Posts\Events\PostCreated;
    use App\Repositories\PostRepository;
    
    class UpdatePostIndex
    {
        public function __invoke(PostCreated $event)
        {
            // Update Elasticsearch, cache, etc.
        }
    }
    

    Register the listener in EventSourcingServiceProvider:

    Event::listen(PostCreated::class, UpdatePostIndex::new());
    
  4. Replaying Events (Recovering State) Reconstruct an aggregate from its event history:

    $postId = 'post-123';
    $post = Post::recover($postId); // Hydrates from event store
    
  5. Snapshotting (Optimization) Use snapshots to reduce event replay time:

    $post->snapshot(); // Stores current state to skip replaying old events
    

Gotchas and Tips

Pitfalls

  1. Event Ordering

    • Events must be replayed chronologically. Ensure your storage guarantees order (e.g., database transactions with ORDER BY).
    • Fix: Use UUIDs for event IDs and sort by occurred_on timestamp.
  2. Concurrency Conflicts

    • Concurrent writes to the same aggregate can cause conflicts.
    • Fix: Implement optimistic locking via expectedVersion in AggregateRoot:
      $post->create($title, $content, expectedVersion: 2);
      
  3. Storage Bloat

    • Event stores grow indefinitely. Clean up old events with:
      Event::cleanupOldEvents(Carbon::now()->subDays(30));
      
  4. Event Versioning

    • Breaking changes to events (e.g., renaming fields) require migration.
    • Tip: Use event_version in the event store table to handle backward compatibility.
  5. Testing

    • Mock the event store in tests:
      $eventStore = Mockery::mock(EventStorageInterface::class);
      $this->app->instance(EventStorageInterface::class, $eventStore);
      

Debugging Tips

  • Event Dump: Enable debug logging in config/event-sourcing.php:
    'debug' => env('APP_DEBUG', false),
    
  • Check Storage: Verify events are persisted:
    php artisan tinker
    >>> \Dddominio\EventSourcing\Facades\Event::all();
    

Extension Points

  1. Custom Storage Implement EventStorageInterface for non-database backends (e.g., Kafka, DynamoDB):

    class CustomEventStorage implements EventStorageInterface
    {
        public function append(string $aggregateId, array $events): void
        {
            // Custom logic (e.g., Kafka producer)
        }
    }
    
  2. Event Serialization Override serialization in AggregateRoot:

    protected function serializeEvent(DomainEvent $event): string
    {
        return json_encode([
            'event' => get_class($event),
            'data' => $event,
        ]);
    }
    
  3. Domain Event Bus Extend the event bus to add middleware:

    Event::extend(function ($bus) {
        $bus->pipe(function ($event, $next) {
            // Pre-process event (e.g., logging)
            return $next($event);
        });
    });
    
  4. Aggregate Factories Use factories to instantiate aggregates with preloaded events:

    $post = Post::recover($postId, new PostFactory());
    
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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin