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

Message Repository Table Schema Laravel Package

eventsauce/message-repository-table-schema

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package

    composer require eventsauce/message-repository-table-schema
    
  2. Generate the Migration Run the Artisan command to create a migration file for the event repository table:

    php artisan generate:message-repository
    

    This creates a migration with a schema optimized for event sourcing, including columns for:

    • aggregate_id (UUID)
    • message_type (string)
    • payload (JSON)
    • occurred_on (timestamp)
    • metadata (JSON)
  3. Run the Migration

    php artisan migrate
    
  4. First Integration Use the repository in a Laravel service or command:

    use EventSauce\EventSourcing\MessageRepository;
    use EventSauce\MessageRepositoryTableSchema\MySqlMessageRepository;
    
    $repository = new MySqlMessageRepository(
        \DB::connection()->getPdo(),
        config('message-repository-table-schema.table_name')
    );
    
    // Store an event
    $event = new OrderCreated('order-123', ['user_id' => 1]);
    $repository->save($event);
    

Implementation Patterns

Laravel Integration Workflow

  1. Service Provider Setup Register the repository in Laravel’s service container:

    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(MessageRepository::class, function ($app) {
            return new MySqlMessageRepository(
                $app['db']->connection()->getPdo(),
                config('message-repository-table-schema.table_name')
            );
        });
    }
    
  2. Event Dispatching Bridge Laravel’s events to EventSauce events:

    // Example: Dispatch a Laravel event and store it as an EventSauce event
    Event::listen('order.created', function ($event) {
        $domainEvent = new OrderCreated(
            $event->orderId,
            ['user_id' => $event->userId]
        );
        app(MessageRepository::class)->save($domainEvent);
    });
    
  3. Event Replaying Replay events for aggregate reconstruction:

    public function replayEventsForAggregate(string $aggregateId)
    {
        $events = app(MessageRepository::class)->getMessagesForAggregate($aggregateId);
        foreach ($events as $event) {
            $this->applyEvent($event);
        }
    }
    
  4. Querying Events Use raw queries or a repository layer for filtering:

    // Example: Find events for an aggregate
    $events = DB::table('message_store')
        ->where('aggregate_id', $aggregateId)
        ->orderBy('occurred_on')
        ->get();
    

Common Patterns

  • Aggregate Root Management Use the repository to load and persist aggregate roots:

    $aggregate = AggregateRoot::fromHistory(
        $repository->getMessagesForAggregate($aggregateId)
    );
    $aggregate->apply(new OrderCancelled());
    $repository->save($aggregate->pullEvents());
    
  • Event Projections Materialize read models from stored events:

    // Example: Project events to a Laravel model
    $events = $repository->getMessagesForAggregate($aggregateId);
    foreach ($events as $event) {
        $this->projectEventToModel($event);
    }
    
  • Metadata Handling Attach custom metadata to events:

    $event = new OrderCreated('order-123', ['user_id' => 1]);
    $event->setMetadata(['source' => 'web', 'version' => '1.0']);
    $repository->save($event);
    

Gotchas and Tips

Pitfalls

  1. Schema Migrations

    • Issue: Adding new columns to the event table requires downtime during migrations.
    • Fix: Use Laravel’s Schema::table() with caution, or implement zero-downtime migration strategies (e.g., adding nullable columns first).
  2. Event Serialization

    • Issue: Events must be serializable to JSON. Complex PHP objects (e.g., with circular references) may fail.
    • Fix: Implement __serialize()/__unserialize() or use a library like jms/serializer.
  3. Performance with Large Payloads

    • Issue: Storing large JSON payloads in a single column can bloat the database.
    • Fix: Normalize event data or use Laravel’s jsonb type (PostgreSQL) for efficient querying.
  4. Concurrency Conflicts

    • Issue: Concurrent writes to the same aggregate may cause race conditions.
    • Fix: Use optimistic locking via metadata (e.g., version field) or Laravel’s lockForUpdate().
  5. Laravel’s Eloquent Conflicts

    • Issue: The package’s schema may conflict with Laravel’s default event storage (e.g., failed_jobs).
    • Fix: Rename tables or use a dedicated database connection for event sourcing.

Debugging Tips

  • Verify Event Storage Check the message_store table directly:

    php artisan tinker
    >>> \DB::table('message_store')->latest()->first();
    
  • Enable Query Logging Debug slow queries:

    DB::enableQueryLog();
    $repository->save($event);
    dd(DB::getQueryLog());
    
  • Test Event Replay Validate replay logic with a small dataset:

    $events = $repository->getMessagesForAggregate($aggregateId);
    $this->assertCount(3, $events); // Example assertion
    

Extension Points

  1. Custom Table Names Override the default table name in config:

    'table_name' => 'custom_event_store',
    
  2. Additional Columns Extend the schema via migrations:

    Schema::table('message_store', function (Blueprint $table) {
        $table->string('event_source')->nullable();
    });
    
  3. Event Filtering Create a repository decorator for custom queries:

    class FilteredMessageRepository implements MessageRepository
    {
        public function getMessagesForAggregate(string $aggregateId, string $eventType = null)
        {
            // Add custom filtering logic
        }
    }
    
  4. Laravel Event Bridge Automate event conversion:

    // app/Listeners/ConvertLaravelEventToDomainEvent.php
    public function handle($event)
    {
        $domainEvent = $this->convert($event);
        app(MessageRepository::class)->save($domainEvent);
    }
    

Configuration Quirks

  • Doctrine DBAL Dependency The package uses Doctrine DBAL under the hood. Ensure your Laravel app includes it:

    composer require doctrine/dbal
    
  • UUID Handling The schema expects UUIDs. Use Laravel’s Ramsey\Uuid package for consistency:

    composer require ramsey/uuid
    
  • Timezone Awareness Ensure occurred_on timestamps use UTC to avoid timezone-related replay issues:

    $event->setOccurredOn(new \DateTimeImmutable('now', new \DateTimeZone('UTC')));
    
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.
craftcms/url-validator
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony