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

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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
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
spatie/mailcoach-vapor