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

Eventsauce Bundle Laravel Package

andreo/eventsauce-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require andreo/eventsauce-bundle
    

    Enable in config/bundles.php:

    Andreo\EventSauceBundle\AndreoEventSauceBundle::class => ['all' => true],
    
  2. Configure Doctrine Event Store Add to config/packages/andreo_event_sauce.yaml:

    andreo_event_sauce:
        message_storage:
            repository:
                doctrine_3:
                    enabled: true
                    table_name: event_store
    
  3. First Use Case: Event Sourcing Define an aggregate root (e.g., FooAggregate) and events (e.g., FooCreated). Use the EventSauce\EventSourcing\AggregateRoot trait:

    use EventSauce\EventSourcing\AggregateRoot;
    
    final class FooAggregate extends AggregateRoot
    {
        public function create(string $name): void
        {
            $this->recordThat(new FooCreated($name));
        }
    }
    

    Persist the aggregate:

    $aggregate = new FooAggregate();
    $aggregate->create('Test');
    $repository->persist($aggregate);
    

Implementation Patterns

Workflows

  1. Event-Driven Projections Use EventConsumer to react to events (e.g., update read models):

    #[AsSyncMessageConsumer(dispatcher: 'foo_dispatcher')]
    final class FooProjectionConsumer extends EventConsumer
    {
        use InjectedHandleMethodInflector;
    
        public function onFooCreated(FooCreated $event, Message $message): void
        {
            // Update read model
        }
    }
    
  2. Anti-Corruption Layer (ACL) Translate external events to domain events:

    #[AsMessageTranslator]
    final readonly class ExternalToDomainTranslator implements MessageTranslator
    {
        public function translateMessage(Message $message): Message
        {
            return new Message(new DomainEvent($message->payload()));
        }
    }
    
  3. Messenger Integration Dispatch events via Symfony Messenger:

    andreo_event_sauce:
        message_dispatcher:
            foo_dispatcher:
                type:
                    messenger:
                        bus: event_bus
    

    Configure the bus in framework/messenger:

    buses:
        event_bus:
            middleware:
                - event_bus.handle_eventsauce_message
    
  4. Snapshotting Enable snapshots for performance:

    andreo_event_sauce:
        snapshot:
            enabled: true
            repository:
                doctrine:
                    enabled: true
                    table_name: snapshot_store
    

    Annotate aggregates:

    #[Snapshot]
    final class FooAggregate extends AggregateRoot { ... }
    
  5. Upcasting Handle versioned events:

    #[AsUpcaster(aggregateClass: FooAggregate::class, version: 2)]
    final class FooEventV2Upcaster implements Upcaster
    {
        public function upcast(array $message): array { ... }
    }
    

Integration Tips

  • Doctrine Migrations: Run php bin/console doctrine:migrations:diff after defining aggregates.
  • Testing: Use EventSauce\EventSourcing\Testing\AggregateRootMother for fixtures.
  • Logging: Enable debug mode in config/packages/dev/andreo_event_sauce.yaml:
    debug: true
    

Gotchas and Tips

Pitfalls

  1. Message Dispatcher Order

    • Dispatchers are chained; ensure messenger dispatchers are placed after send_message middleware in Symfony Messenger config.
    • Example error: Events dispatched via Messenger may not trigger ACL if the dispatcher is misconfigured.
  2. ACL Owners Misconfiguration

    • If a MessageTranslator or MessageFilter is tagged with incorrect owners, it won’t apply to intended consumers/dispatchers.
    • Fix: Explicitly define owners in attributes or tags:
      tags:
          name: andreo.eventsauce.acl.message_translator
          owners: [App\Consumer\FooConsumer]
      
  3. Snapshot Versioning

    • Versioned snapshots require versioned: true in config and proper SnapshotVersionInflector setup.
    • Debug: Check if snapshots are saved by inspecting the snapshot_store table.
  4. Upcasting Timing

    • before_unserialize upcasters modify raw event data; after_unserialize (via eventsauce-upcasting) work on hydrated objects.
    • Test with EventSauce\EventSourcing\Testing\AggregateRootMother::withEvents().
  5. Outbox Relay ID

    • The relay_id in message_outbox must match the command used to consume messages:
      bin/console andreo:eventsauce:message-outbox:consume event_dispatcher_relay
      
    • Omitting it causes silent failures.

Debugging

  • Event Not Fired? Check if the aggregate’s recordThat() is called and the event is serialized. Enable debug logs:

    andreo_event_sauce:
        debug: true
    
  • Consumer Not Triggered? Verify the consumer is tagged with andreo.eventsauce.sync_message_consumer or annotated with #[AsSyncMessageConsumer]. Test with:

    bin/console debug:container App\Consumer\FooConsumer
    
  • ACL Not Applied? Ensure the translator/filter is registered with the correct owners and trigger (e.g., before_translate).

Extension Points

  1. Custom Message Decorators Extend EventSauce\EventSourcing\MessageDecorator to add metadata (e.g., tenant IDs):

    #[AsMessageDecorator]
    final class TenantDecorator implements MessageDecorator
    {
        public function decorate(Message $message): Message
        {
            return new Message($message->payload(), $message->metadata() + ['tenant_id' => '123']);
        }
    }
    
  2. Dynamic Dispatchers Use Symfony’s autoconfigure: true to auto-register dispatchers via attributes:

    services:
        _defaults:
            autoconfigure: true
    
  3. Custom Upcasters Implement EventSauce\EventSourcing\Upcasting\Upcaster for aggregate-specific logic:

    #[AsUpcaster(aggregateClass: OrderAggregate::class, version: 3)]
    final class OrderV3Upcaster implements Upcaster { ... }
    
  4. Event Outbox Relays Extend EventSauce\MessageOutbox\Relay to support custom transport (e.g., Kafka):

    use EventSauce\MessageOutbox\Relay;
    
    final class KafkaRelay implements Relay { ... }
    

Configuration Quirks

  • Timezone Mismatch: Ensure andreo_event_sauce.clock.timezone matches your app’s timezone to avoid event timestamp issues.
  • Doctrine Connection: Specify connection in doctrine_3 config if using multiple DBs:
    connection: doctrine.dbal.secondary_connection
    
  • Messenger Bus Alias: The bus key in messenger dispatchers must match Symfony’s framework.messenger.buses config.
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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware