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

Simple Bus On Steroids Laravel Package

cleancode/simple-bus-on-steroids

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require cleancode/simple-bus-on-steroids
    

    Ensure simplebus/simple-bus and doctrine/dbal (or another DBAL-compatible package) are also installed.

  2. Configuration: Publish the config file:

    php artisan vendor:publish --provider="CleanCode\SimpleBusOnSteroids\SimpleBusOnSteroidsServiceProvider"
    

    Update .env with your RabbitMQ and database settings (e.g., SIMPLE_BUS_ON_STEROIDS_* keys).

  3. First Use Case:

    • Publish an event:
      use CleanCode\SimpleBusOnSteroids\Event\EventBus;
      use CleanCode\SimpleBusOnSteroids\Event\Event;
      
      $eventBus = app(EventBus::class);
      $event = new Event('user.created', ['user_id' => 123]);
      $eventBus->publish($event);
      
    • Subscribe to an event:
      use CleanCode\SimpleBusOnSteroids\Event\EventSubscriber;
      
      class UserCreatedSubscriber implements EventSubscriber
      {
          public function handle(Event $event): void
          {
              // Handle the event (e.g., send email, update analytics)
          }
      
          public static function subscribedTo(): string
          {
              return 'user.created';
          }
      }
      

    Register the subscriber in config/simple_bus_on_steroids.php under subscribers.

  4. Run the Async Worker:

    php artisan simple-bus-on-steroids:work
    

    This processes events from the database and pushes them to RabbitMQ.


Implementation Patterns

Core Workflows

  1. Event Publishing with Transactions:

    • Events are automatically saved to the database within the same transaction as the entity they originate from.
    • Example:
      DB::transaction(function () use ($user, $eventBus) {
          $user->save();
          $eventBus->publish(new Event('user.created', ['user_id' => $user->id]));
      });
      
  2. Async Rabbit Pattern:

    • The simple-bus-on-steroids:work command fetches events from the database and pushes them to RabbitMQ in batches.
    • Configure batch size and delay in config/simple_bus_on_steroids.php:
      'async_rabbit' => [
          'batch_size' => 100,
          'delay_seconds' => 1,
      ],
      
  3. Handling Events with Retries and Dead Letters:

    • Failed events are automatically requeued with a configurable delay (default: 5 seconds).
    • After max_retries (default: 3), events are moved to a dead-letter queue (e.g., a separate RabbitMQ queue or database table).
    • Configure in config/simple_bus_on_steroids.php:
      'retry' => [
          'delay_seconds' => 5,
          'max_retries' => 3,
      ],
      'dead_letter' => [
          'queue' => 'dead_letter_events',
          'table' => 'failed_events', // Optional: for DB-based dead letter
      ],
      
  4. Event Correlation and Tracing:

    • Events are decorated with metadata (e.g., event_id, correlation_id, parent_id, occurrence_time).
    • Useful for debugging and event-sourcing:
      $event = new Event('order.placed', ['order_id' => 456], [
          'description' => 'User placed an order',
          'correlation_id' => 'user-123-order-456',
      ]);
      
  5. Subscriber Isolation:

    • Each subscriber runs in its own transaction. If one fails, others continue processing.
    • Example subscriber with error handling:
      class OrderPlacedSubscriber implements EventSubscriber
      {
          public function handle(Event $event): void
          {
              try {
                  // Business logic
              } catch (\Exception $e) {
                  // Log the error (e.g., using Laravel's logger)
                  \Log::error("Failed to handle order.placed: " . $e->getMessage());
                  throw $e; // Will trigger retry logic
              }
          }
      
          public static function subscribedTo(): string
          {
              return 'order.placed';
          }
      }
      

Integration Tips

  1. Laravel Integration:

    • Bind the EventBus to the container in AppServiceProvider:
      public function register()
      {
          $this->app->bind(EventBus::class, function ($app) {
              return new EventBus(
                  $app->make(Doctrine\DBAL\Connection::class),
                  $app->make(RabbitMQClient::class),
                  config('simple_bus_on_steroids')
              );
          });
      }
      
  2. Event-Sourcing:

    • Use the description field to reconstruct event-sourced entities:
      $event = new Event('user.updated', ['user_id' => 123], [
          'description' => json_encode(['old_email' => 'old@example.com', 'new_email' => 'new@example.com']),
      ]);
      
  3. Testing:

    • Mock the EventBus in tests:
      $mockEventBus = Mockery::mock(EventBus::class);
      $mockEventBus->shouldReceive('publish')->once();
      $this->app->instance(EventBus::class, $mockEventBus);
      
  4. Monitoring:

    • Track failed events in the dead-letter queue (database or RabbitMQ) and set up alerts for high failure rates.

Gotchas and Tips

Pitfalls

  1. Database Locking:

    • The async worker fetches events in batches from the database. If the batch size is too large, it may cause long-running transactions or locks.
    • Fix: Keep batch_size small (e.g., 10-50) and ensure your database can handle the load.
  2. RabbitMQ Connection Issues:

    • If RabbitMQ is unreachable, the async worker will fail silently or throw exceptions.
    • Fix: Implement retry logic for RabbitMQ connections or use a circuit breaker pattern.
  3. Event Ordering:

    • Events are processed asynchronously, so there’s no guarantee of order unless you use correlation_id and parent_id to manually enforce relationships.
    • Tip: Use correlation_id to group related events (e.g., all events for a single order).
  4. Dead-Letter Queue Overload:

    • If too many events fail, the dead-letter queue (database or RabbitMQ) can grow uncontrollably.
    • Fix: Set up a cron job to periodically clean up old failed events or archive them.
  5. Transaction Timeouts:

    • Long-running subscribers may hit database transaction timeouts.
    • Fix: Optimize subscriber logic or break it into smaller transactions.

Debugging

  1. Check Event Status:

    • Query the events table to see the status of published events:
      SELECT * FROM events WHERE event_name = 'user.created' ORDER BY occurrence_time DESC;
      
    • Look for status values like pending, processed, or failed.
  2. Log Subscriber Errors:

    • Wrap subscriber logic in try-catch blocks to log errors:
      try {
          // Subscriber logic
      } catch (\Exception $e) {
          \Log::error("Subscriber failed: " . $e->getMessage(), [
              'event' => $event->getName(),
              'payload' => $event->getPayload(),
          ]);
          throw $e;
      }
      
  3. RabbitMQ Debugging:

    • Use the RabbitMQ management UI (http://localhost:15672) to inspect queues, messages, and consumer activity.
    • Check for unacknowledged messages or dead-letter exchanges.
  4. Event Decoration:

    • Ensure event_id, correlation_id, and parent_id are set correctly for tracing. Use UUIDs for event_id:
      $event = new Event('user.created', ['user_id' => 123], [
          'event_id' => Str::uuid()->toString(),
          'correlation_id' => 'user-123',
      ]);
      

Configuration Quirks

  1. Default Values:

    • The package uses sensible defaults, but always verify config/simple_bus_on_steroids.php for:
      • retry.delay_seconds: Default is 5 seconds.
      • retry.max_retries: Default is 3.
      • dead_letter.queue: Must match your RabbitMQ dead-letter exchange/queue.
  2. Database Schema:

    • The package expects specific tables (events, failed_events). Run migrations if they’re not present:
      php artisan vendor:publish --provider="CleanCode\SimpleBusOnSteroids\Database\MigrationsServiceProvider"
      php artisan migrate
      
  3. Async Worker Command:

    • The worker runs indefinitely. For testing, use --once to process
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.
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
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager