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

Lite Cqrs Laravel Package

beberlei/lite-cqrs

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require beberlei/lite-cqrs:1.1
    

    Use the 1.1 branch as dev-master is in active refactoring.

  2. First Command:

    // Define a command (no interface needed)
    class CreateUserCommand {
        public $name;
        public $email;
    }
    
    // Define a handler
    class UserCommandHandler {
        public function createUser(CreateUserCommand $command) {
            // Business logic here
            return new UserCreatedEvent($command->name, $command->email);
        }
    }
    
    // Register and dispatch
    $commandBus = new \LiteCQRS\Bus\DirectCommandBus();
    $commandBus->register('CreateUserCommand', new UserCommandHandler());
    
    $command = new CreateUserCommand();
    $command->name = 'John Doe';
    $command->email = 'john@example.com';
    
    $commandBus->dispatch($command);
    
  3. First Event:

    // Define an event
    class UserCreatedEvent {
        public $name;
        public $email;
    }
    
    // Define an event handler
    class UserEventHandler {
        public function onUserCreated(UserCreatedEvent $event) {
            // Side effects (e.g., notifications, analytics)
        }
    }
    
    // Register the event handler
    $eventBus = new \LiteCQRS\Bus\InMemoryEventMessageBus();
    $eventBus->register(new UserEventHandler());
    

Where to Look First

  • Conventions: Focus on naming (e.g., CreateUserCommandcreateUser() method).
  • Examples: Study example1.php for a full workflow.
  • Symfony Integration: If using Symfony, prioritize the LiteCQRSBundle.

Implementation Patterns

Command Workflow

  1. Command Creation: Use DTO-like classes with public properties (no getters/setters needed). Example:

    class UpdateUserEmailCommand {
        public $userId;
        public $newEmail;
    }
    
  2. Handler Registration: Register handlers via the CommandBus using the class shortname as the key.

    $commandBus->register('UpdateUserEmailCommand', $userService);
    
  3. Event Emission: Emit events from handlers using AggregateRoot::apply() (for event sourcing) or DomainEventProvider::raise() (for simple pub/sub).

    class UserService {
        public function updateEmail(UpdateUserEmailCommand $command) {
            $user = $this->userRepository->find($command->userId);
            $user->apply(new UserEmailUpdatedEvent($command->newEmail));
        }
    }
    
  4. Event Handling: Implement onEventName() methods in event handlers.

    class EmailNotificationHandler {
        public function onUserEmailUpdated(UserEmailUpdatedEvent $event) {
            Mail::send('emails.user_updated', ['email' => $event->newEmail]);
        }
    }
    

Integration Tips

  • Dependency Injection: Use Laravel’s service container to bind commands/handlers:

    $this->app->bind('UpdateUserEmailCommand', function () {
        return new UpdateUserEmailCommand();
    });
    
  • Middleware: Wrap the CommandBus in middleware for logging, validation, or transactions:

    class CommandLoggingMiddleware {
        public function handle($command, Closure $next) {
            Log::info("Dispatching: " . get_class($command));
            return $next($command);
        }
    }
    
  • Event Sourcing: For aggregates, implement AggregateRoot and apply() methods:

    class User implements AggregateRoot {
        public function apply(UserEmailUpdatedEvent $event) {
            $this->email = $event->newEmail;
        }
    }
    
  • Sequential Commands: Use SequentialCommandBus to ensure commands execute in order:

    $commandBus = new \LiteCQRS\Bus\SequentialCommandBus();
    

Gotchas and Tips

Pitfalls

  1. Naming Conventions:

    • Commands: Must match handler method names exactly (e.g., CreateUserCommandcreateUser()). Fix: Use IDE refactoring to rename classes/methods in sync.
    • Events: Handler methods must start with on (e.g., onUserCreated()). Fix: Rename methods if the convention is violated.
  2. Event Sourcing:

    • Forgetting to implement apply() for events breaks replayability. Fix: Use AggregateRoot trait and define applyEventName() methods.
  3. Circular Dependencies:

    • Event handlers creating commands can lead to infinite loops. Fix: Use a CommandBus with a queue (e.g., InMemoryCommandBus) to track in-flight commands.
  4. Transaction Boundaries:

    • Events are published after the command completes (even if it fails). Fix: Use EventMessageHandlerFactory with a transactional CommandBus.
  5. Symfony Autowiring:

    • The LiteCQRSBundle requires explicit tags (lite_cqrs.command_handler). Fix: Add tags to service definitions:
      services:
          App\CommandHandlers\UserCommandHandler:
              tags: ['lite_cqrs.command_handler']
      

Debugging

  • Failed Events: Listen for EventExecutionFailed to catch handler errors:

    $eventBus->register(new class {
        public function onEventExecutionFailed(EventExecutionFailed $event) {
            Log::error("Event failed: " . $event->exception->getMessage());
        }
    });
    
  • Command Not Found: Ensure the handler method name matches the command’s short class name. Debug: Use DirectCommandBus::getHandler() to inspect registrations.

  • Event Not Triggered: Verify the event class shortname matches the handler method (e.g., UserCreatedEventonUserCreated()).

Extension Points

  1. Custom CommandBus: Extend \LiteCQRS\Bus\CommandBus to add pre/post-processing:

    class LoggingCommandBus extends \LiteCQRS\Bus\CommandBus {
        public function dispatch($command) {
            Log::info("Dispatching: " . get_class($command));
            parent::dispatch($command);
        }
    }
    
  2. Custom EventQueue: Implement \LiteCQRS\Bus\EventQueue for async processing (e.g., Redis):

    class RedisEventQueue implements EventQueue {
        public function getEvents() { /* ... */ }
        public function clear() { /* ... */ }
    }
    
  3. Monolog Integration: Enable logging via the MonologPlugin:

    $monologPlugin = new \LiteCQRS\Plugin\MonologPlugin($logger);
    $commandBus->addHandler($monologPlugin);
    

Laravel-Specific Tips

  • Service Providers: Register the CommandBus and EventBus in a provider:

    public function register() {
        $this->app->singleton(\LiteCQRS\Bus\CommandBus::class, function () {
            $bus = new \LiteCQRS\Bus\DirectCommandBus();
            $bus->register('CreateUserCommand', $this->app->make(UserCommandHandler::class));
            return $bus;
        });
    }
    
  • Artisan Commands: Use the CommandBus to trigger commands from CLI:

    class SendWelcomeEmailCommand extends Command {
        protected $signature = 'cqrs:send-welcome {userId}';
        public function handle() {
            $commandBus = app(\LiteCQRS\Bus\CommandBus::class);
            $commandBus->dispatch(new SendWelcomeEmailCommand($this->argument('userId')));
        }
    }
    
  • Testing: Mock the CommandBus and EventBus in tests:

    $mockBus = Mockery::mock(\LiteCQRS\Bus\CommandBus::class);
    $mockBus->shouldReceive('dispatch')->once();
    $this->app->instance(\LiteCQRS\Bus\CommandBus::class, $mockBus);
    
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