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

Symfony Bundle Laravel Package

ecotone/symfony-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require ecotone/ecotone
    

    Ensure ecotone/ecotone is listed in composer.json under require.

  2. Bundle Configuration Add to config/bundles.php:

    return [
        // ...
        Ecotone\SymfonyBundle\EcotoneBundle::class => ['all' => true],
    ];
    
  3. First Use Case: Command Handler Define a command and handler using attributes:

    // src/Command/ProcessOrderCommand.php
    #[Command]
    class ProcessOrderCommand {
        public function __construct(public string $orderId) {}
    }
    
    // src/CommandHandler/ProcessOrderCommandHandler.php
    #[CommandHandler]
    class ProcessOrderCommandHandler {
        public function __construct(private OrderRepository $orderRepo) {}
    
        #[Transactional]
        public function __invoke(ProcessOrderCommand $command) {
            $order = $this->orderRepo->find($command->orderId);
            // Business logic here
        }
    }
    
  4. Dispatching Commands Use Symfony’s Messenger component to dispatch:

    $this->messageBus->dispatch(new ProcessOrderCommand('order-123'));
    

Where to Look First

  • Ecotone Docs (Official documentation for patterns and attributes).
  • src/Ecotone/SymfonyBundle/Resources/config/services.yaml (Default bundle configuration).
  • tests/ in the Ecotone-Dev repo for real-world examples.

Implementation Patterns

Core Workflows

1. CQRS with Command/Query Separation

  • Commands: Use #[Command] and #[CommandHandler] for write operations.
    #[Command]
    class UpdateUserEmailCommand { ... }
    
    #[CommandHandler]
    class UpdateUserEmailHandler {
        public function __invoke(UpdateUserEmailCommand $command) { ... }
    }
    
  • Queries: Use #[Query] and #[QueryHandler] for read operations.
    #[Query]
    class GetUserByEmailQuery { ... }
    
    #[QueryHandler]
    class GetUserByEmailHandler {
        public function __invoke(GetUserByEmailQuery $query) { ... }
    }
    
  • Dispatch Queries:
    $user = $this->queryBus->ask(new GetUserByEmailQuery('user@example.com'));
    

2. Event Sourcing

  • Aggregate Roots: Use #[AggregateRoot] and #[EventSourced].
    #[AggregateRoot]
    #[EventSourced]
    class Order {
        public function apply(OrderCreatedEvent $event) { ... }
        public function apply(OrderPaidEvent $event) { ... }
    }
    
  • Events: Define events with #[Event].
    #[Event]
    class OrderCreatedEvent { ... }
    
  • Event Handlers: Use #[EventHandler] to react to events.
    #[EventHandler]
    class NotifyOrderCreatedHandler {
        public function __invoke(OrderCreatedEvent $event) { ... }
    }
    

3. Sagas for Long-Running Processes

  • Define sagas with #[Saga] and #[SagaStep].
    #[Saga]
    class OrderFulfillmentSaga {
        #[SagaStep]
        public function handleOrderCreated(OrderCreatedEvent $event) { ... }
    
        #[SagaStep]
        public function handlePaymentConfirmed(PaymentConfirmedEvent $event) { ... }
    }
    

4. Workflows for State Machines

  • Use #[Workflow] and #[WorkflowStep] for complex state transitions.
    #[Workflow]
    class OrderWorkflow {
        #[WorkflowStep]
        public function validateOrder(Order $order) { ... }
    
        #[WorkflowStep]
        public function processPayment(Order $order) { ... }
    }
    

Integration Tips

  • Symfony Messenger: Ecotone leverages Symfony Messenger under the hood. Configure transports in config/packages/messenger.yaml:
    framework:
        messenger:
            transports:
                async: '%env(MESSENGER_TRANSPORT_DSN)%'
    
  • Doctrine Integration: Use #[Transactional] for automatic Doctrine transactions:
    #[CommandHandler]
    class ProcessOrderHandler {
        #[Transactional]
        public function __invoke(ProcessOrderCommand $command) { ... }
    }
    
  • Outbox Pattern: Enable via configuration to ensure reliable event publishing:
    ecotone:
        outbox:
            enabled: true
            table_name: 'outbox_messages'
    

Gotchas and Tips

Pitfalls

  1. Attribute Conflicts

    • Avoid mixing Ecotone attributes with Symfony Messenger’s native ones (e.g., #[AsMessage]). Ecotone attributes are designed to be self-contained.
    • Fix: Stick to Ecotone’s attribute set (#[Command], #[Event], etc.).
  2. Event Sourcing Snapshotting

    • Large aggregates may suffer from performance issues with event sourcing. Use #[Snapshot] to optimize:
      #[AggregateRoot]
      #[EventSourced]
      #[Snapshot(every: 10)] // Take a snapshot every 10 events
      class LargeAggregate { ... }
      
  3. Saga Timeout Handling

    • Sagas without proper timeout handling may hang indefinitely. Configure timeouts in config/packages/ecotone.yaml:
      ecotone:
          sagas:
              timeout: 3600 # 1 hour in seconds
      
  4. Query Bus vs. Command Bus

    • Mistake: Dispatching a #[Query] as a command (e.g., using messageBus->dispatch() instead of queryBus->ask()).
    • Fix: Always use #[QueryHandler] with queryBus->ask() for read operations.
  5. Transactional Boundaries

    • #[Transactional] applies to the entire handler method. Avoid long-running transactions:
      #[CommandHandler]
      class LongRunningHandler {
          #[Transactional] // Risk of timeout
          public function __invoke(LongRunningCommand $command) {
              // Offload heavy work to async tasks
          }
      }
      

Debugging

  1. Enable Debugging Add to config/packages/ecotone.yaml:

    ecotone:
        debug: true
    

    This logs dispatched messages, event sourcing snapshots, and saga steps.

  2. Check Middleware Ecotone uses middleware for features like retries, logging, and outbox. Inspect config/packages/ecotone.yaml for middleware configuration:

    ecotone:
        middleware:
            - 'ecotone.middleware.retry'
            - 'ecotone.middleware.outbox'
    
  3. Event Sourcing Debugging

    • Use #[EventSourced] with debug: true to log applied events:
      #[EventSourced(debug: true)]
      class Order { ... }
      

Extension Points

  1. Custom Middleware Create middleware to intercept messages:

    use Ecotone\Middleware\Middleware;
    
    class LoggingMiddleware implements Middleware {
        public function handle(object $message, callable $next) {
            // Log before
            $result = $next($message);
            // Log after
            return $result;
        }
    }
    

    Register in config/packages/ecotone.yaml:

    ecotone:
        middleware:
            - 'App\Middleware\LoggingMiddleware'
    
  2. Custom Event Stores Extend EventStore interface to use a non-Doctrine database:

    use Ecotone\EventSourcing\EventStore;
    
    class CustomEventStore implements EventStore {
        public function load(string $aggregateId): ?AggregateRoot { ... }
        public function save(AggregateRoot $aggregate): void { ... }
    }
    

    Bind in Symfony’s services:

    services:
        Ecotone\EventSourcing\EventStoreInterface: '@App\CustomEventStore'
    
  3. Dynamic Command/Query Resolution Override CommandBus or QueryBus to add dynamic resolution logic:

    use Ecotone\CommandHandling\CommandBus;
    
    class CustomCommandBus implements CommandBus {
        public function dispatch(object $command): void {
            // Custom logic
        }
    }
    

    Bind in config/services.yaml:

    services:
        Ecotone\CommandHandling\CommandBusInterface: '@App\CustomCommandBus'
    

Configuration Quirks

  1. Outbox Table Requirements Ensure the outbox table exists and matches the schema. Run migrations if using Doctrine:
    php bin/console doctrine:migrations:diff
    php bin/console doctrine:migrations:migrate
    
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui