ahmedosamir732/eventbus-php

A Laravel package for publishing and consuming integration events over RabbitMQ with built-in retry, DLQ, and multi-tenant support.

Maintainers

Package info

github.com/ahmedsamir732/eventbus-php

pkg:composer/ahmedosamir732/eventbus-php

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-06-16 11:19 UTC

This package is auto-updated.

Last update: 2026-06-16 12:29:49 UTC


README

A Laravel package for publishing and consuming integration events over RabbitMQ. Supports multi-tenant contexts, automatic retry with exponential backoff, dead-letter queues, and graceful shutdown.

Requirements

  • PHP 8.3+
  • Laravel 11 or 12
  • RabbitMQ 3.7+

Installation

composer require ahmedosamir732/eventbus-php

Publish the config:

php artisan vendor:publish --tag=eventbus-config

Configuration

Add these to your .env:

RABBITMQ_HOST=rabbitmq
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_VHOST=/
EVENTBUS_SOURCE=my-service
EVENTBUS_DRIVER=rabbitmq

The driver defaults to rabbitmq when RABBITMQ_HOST is set, otherwise falls back to null (no-op).

Publishing Events

Implement ContextProviderInterface to supply tenant, workspace, actor, and correlation info from the current request:

use Idaratech\EventBus\Contracts\ContextProviderInterface;

class AppContextProvider implements ContextProviderInterface
{
    public function getTenantId(): int|string|null { /* ... */ }
    public function getWorkspace(): string { /* ... */ }
    public function getActor(): array { /* ... */ }
    public function getCorrelationId(): ?string { /* ... */ }
}

Register it in config/eventbus.php:

'context_provider' => App\EventBus\AppContextProvider::class,

Then publish events:

use Idaratech\EventBus\Contracts\EventBusInterface;
use Idaratech\EventBus\IntegrationEventFactory;

class SomeService
{
    public function __construct(
        private EventBusInterface $bus,
        private IntegrationEventFactory $factory,
    ) {}

    public function doWork(): void
    {
        $event = $this->factory->create(
            eventType: 'user.created',
            version: '1.0',
            payload: ['user_id' => 123, 'email' => 'user@example.com'],
        );

        $this->bus->publish('my-exchange', $event);
    }
}

Consuming Events

Implement EventHandlerInterface and tag it in a service provider:

use Idaratech\EventBus\Contracts\EventHandlerInterface;
use Idaratech\EventBus\Contracts\IntegrationEventInterface;

class UserCreatedHandler implements EventHandlerInterface
{
    public function supports(string $eventType): bool
    {
        return $eventType === 'user.created';
    }

    public function handle(IntegrationEventInterface $event): void
    {
        $payload = $event->getPayload();
        // Process the event...
    }
}

Register the handler:

$this->app->tag([UserCreatedHandler::class], 'eventbus.handlers');

Configure consumers in config/eventbus.php:

'consumers' => [
    [
        'queue' => 'my-events',
        'retryExchange' => 'my-events.retry',
        'dlqRoutingKey' => 'my-events.dlq',
        'maxRetries' => 3,
        'retryDelays' => [1000, 5000, 25000],
        'prefetchCount' => 1,
    ],
],

Start the consumer:

php artisan eventbus:consume --memory-limit=128 --timeout=0

DLQ Replay

Replay dead-letter messages:

php artisan eventbus:replay-dlq

Event Envelope

Every event carries a standardized envelope:

Field Type Description
event_id string (UUID) Unique event identifier
event_type string Routing key (e.g. user.created)
version string Schema version
source string Publishing service name
tenant_id int|string|null Tenant context
workspace string Workspace/environment
correlation_id string|null Request trace ID
actor array Who triggered the event
payload array Event-specific data
timestamp string (ISO 8601) When the event was created
metadata array Retry count, original timestamp

Lifecycle Hooks

Register before/after hooks in config:

'hooks' => [
    'before_handle' => function (IntegrationEventInterface $event) {
        // Initialize tenant context, set log context, etc.
    },
    'after_handle' => function (?IntegrationEventInterface $event) {
        // Clean up tenant context, flush logs, etc.
    },
],

NullEventBus

When EVENTBUS_DRIVER=null (or RABBITMQ_HOST is unset), a NullEventBus is bound that silently discards all published events. Useful for local development and testing.

License

MIT