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

Ddd Symfony Bridge Laravel Package

becklyn/ddd-symfony-bridge

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Package

    composer require becklyn/ddd-symfony-bridge
    
  2. Enable Bundles Add to config/bundles.php:

    return [
        // ...
        SimpleBus\SymfonyBridge\SimpleBusCommandBusBundle::class => ['all' => true],
        SimpleBus\SymfonyBridge\SimpleBusEventBusBundle::class => ['all' => true],
        Becklyn\Ddd\BecklynDddBundle::class => ['all' => true],
    ];
    
  3. Configure Auto-Discovery Add to config/services.yaml:

    event_subscribers:
        resource: '../src/**/*Subscriber.php'
        tags: ['event_subscriber']
    
    command_handlers:
        resource: '../src/**/*Handler.php'
        tags: ['command_handler']
    
  4. Run Migrations (if using event store)

    php bin/console doctrine:migrations:migrate
    
  5. Create Your First Command/Event

    • Define a command (e.g., src/Command/CreateUserCommand.php):
      namespace App\Command;
      class CreateUserCommand { /* ... */ }
      
    • Create a handler (e.g., src/Handler/CreateUserCommandHandler.php):
      namespace App\Handler;
      use App\Command\CreateUserCommand;
      class CreateUserCommandHandler {
          public function handle(CreateUserCommand $command) { /* ... */ }
      }
      
    • Dispatch the command in a controller:
      use App\Command\CreateUserCommand;
      use Becklyn\Ddd\CommandBus\CommandBusInterface;
      
      class UserController {
          public function __construct(private CommandBusInterface $commandBus) {}
      
          public function create() {
              $this->commandBus->dispatch(new CreateUserCommand());
          }
      }
      
  6. Create Your First Event Subscriber

    • Define an event (e.g., src/Event/UserCreatedEvent.php):
      namespace App\Event;
      class UserCreatedEvent { /* ... */ }
      
    • Create a subscriber (e.g., src/Subscriber/UserCreatedSubscriber.php):
      namespace App\Subscriber;
      use App\Event\UserCreatedEvent;
      class UserCreatedSubscriber {
          public function handle(UserCreatedEvent $event) { /* ... */ }
      }
      

First Use Case: Event-Driven User Onboarding

  1. Dispatch a CreateUserCommand from a controller.
  2. The command handler creates a user and raises a UserCreatedEvent.
  3. The event subscriber sends a welcome email or logs the event to the store.

Implementation Patterns

1. Command-Handler Workflow

  • Pattern: Use commands to encapsulate business logic invocation.
    • Controller → Dispatches a command.
    • Command Handler → Processes the command (e.g., validates, persists, raises events).
    • Example:
      // Controller
      $this->commandBus->dispatch(new UpdateInventoryCommand($productId, $quantity));
      
      // Handler
      public function handle(UpdateInventoryCommand $command) {
          $product = $this->productRepository->find($command->productId);
          $product->updateQuantity($command->quantity);
          $product->save(); // Raises events like `InventoryUpdatedEvent`
      }
      

2. Event Subscriber Patterns

  • Pattern A: Single Event, Single Subscriber
    class SendWelcomeEmailSubscriber {
        public function handle(UserCreatedEvent $event) {
            $this->mailer->send('welcome', $event->user->email);
        }
    }
    
  • Pattern B: Multiple Events, One Subscriber
    class AuditLoggerSubscriber {
        public function handle(OrderCreatedEvent $event) { /* ... */ }
        public function handle(OrderCancelledEvent $event) { /* ... */ }
    }
    
  • Pattern C: Event Correlation Use causationId and correlationId (from becklyn/ddd-core) to track event chains:
    $eventBus->dispatch(
        new UserCreatedEvent($user),
        causationId: $command->id,
        correlationId: $command->id
    );
    

3. Event Store Integration

  • Pattern: Use the event store for auditability and replayability.
    • Enable in config/packages/becklyn_ddd.yaml:
      becklyn_ddd:
          use_event_store: true
      
    • Query events for an aggregate:
      $events = $eventStore->getEventsForAggregate(
          AggregateId::fromString($aggregateId),
          0, // from version
          10 // limit
      );
      

4. Dependency Injection

  • Pattern: Inject services into handlers/subscribers.
    class CreateOrderHandler {
        public function __construct(
            private OrderRepository $orderRepo,
            private PaymentGateway $paymentGateway
        ) {}
    
        public function handle(CreateOrderCommand $command) {
            $order = $this->orderRepo->create($command->details);
            $this->paymentGateway->charge($order->total());
        }
    }
    

5. Testing Patterns

  • Pattern A: Command Handler Tests
    public function testCreateOrderCommand() {
        $handler = new CreateOrderHandler($this->mockOrderRepo(), $this->mockPaymentGateway());
        $command = new CreateOrderCommand(/* ... */);
    
        $handler->handle($command);
    
        $this->assertTrue($this->mockOrderRepo()->wasCreated());
    }
    
  • Pattern B: Event Subscriber Tests
    public function testUserCreatedSubscriber() {
        $subscriber = new SendWelcomeEmailSubscriber($this->mockMailer());
        $event = new UserCreatedEvent(/* ... */);
    
        $subscriber->handle($event);
    
        $this->assertEmailWasSent();
    }
    
  • Pattern C: Event Store Tests
    public function testEventStorePersistence() {
        $eventStore = $this->createEventStore();
        $event = new UserCreatedEvent(/* ... */);
    
        $eventStore->append($aggregateId, $event);
    
        $storedEvents = $eventStore->getEventsForAggregate($aggregateId);
        $this->assertCount(1, $storedEvents);
    }
    

6. Integration with Symfony Controllers

  • Pattern: Use AggregateIdParamConverter to resolve aggregate IDs from route parameters.
    # config/routes.yaml
    app_order_show:
        path: /orders/{id}
        controller: App\Controller\OrderController::show
        requirements:
            id: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
    
    // Controller
    public function show(AggregateId $id, EventStore $eventStore) {
        $events = $eventStore->getEventsForAggregate($id);
        // Rehydrate aggregate from events
    }
    

7. Async Command Handling

  • Pattern: Use SimpleBus’s async capabilities for long-running tasks.
    # config/packages/command_bus.yaml
    command_bus:
        middlewares:
            finishes_command_before_handling_next: false
    
    • Dispatch a command that triggers another command synchronously:
      public function handle(ProcessPaymentCommand $command) {
          $this->commandBus->dispatch(new SendReceiptEmailCommand($command->orderId));
          // Continue processing payment...
      }
      

Gotchas and Tips

Pitfalls

  1. Naming Conventions

    • Gotcha: Only classes ending in Subscriber or Handler are auto-discovered.
      • Fix: Rename classes or manually tag them in services.yaml:
        tags:
            - { name: event_subscriber, register_public_methods: true }
        
    • Tip: Use a naming convention like *EventSubscriber and *CommandHandler for clarity.
  2. Event Store Migrations

    • Gotcha: Forgetting to run doctrine:migrations:migrate after enabling use_event_store: true causes runtime exceptions.
      • Fix: Always run migrations in your deployment pipeline.
  3. Circular Dependencies

    • Gotcha: Command handlers or subscribers depending on each other can cause deadlocks.
      • Fix: Refactor to share dependencies via constructor injection or a service locator.
  4. Enum Serialization

    • Gotcha: Enums in events/commands may not serialize correctly without the BackedEnumNormalizer.
      • Fix: Add the normalizer to services.yaml (as shown in the docs).
  5. Microsecond Precision

    • Gotcha: Oracle databases require additional configuration for microsecond timestamps.
      • Fix: Include the MicrosecondsOracleSessionInit listener (as shown in the docs).
  6. **Command Handler Uniqu

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
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
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