Search by

uzapoint / eventbus-core

sammy-boycaleb98kymez

Reusable event bus infrastructure for Laravel microservices

Package info

github.com/uzapoint/eventbus-core

pkg:composer/uzapoint/eventbus-core

Statistics

Installs: 1 193

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

v1.3.1 2026-10-27 06:33 UTC

This package is auto-updated.

Last update: 2026-10-27 06:34:03 UTC


README

Reusable Event Bus infrastructure for Laravel microservices, built on RabbitMQ with Redis-backed idempotency.

Repository: https://github.com/uzapoint/eventbus-core

Features

  • Standardized event envelope (meta + data)
  • RabbitMQ publisher with confirmations and bounded timeouts
  • Config-driven consumer that exits on any error, so the supervisor restarts it
  • Retry with backoff, then parking in <queue>.dlq
  • Two-phase idempotency (Redis)
  • Liveness watchdog for hung consumers
  • eventbus:doctor health audit
  • Laravel auto-discovery

Installation

composer require uzapoint/eventbus-core

Requirements: PHP 8.4+, Laravel 11/12, RabbitMQ, Redis (phpredis or predis), pcntl.

Configuration

A service only defines what is specific to it. All other settings come from the package defaults and are merged key by key, so override only what you need (for example 'consumer' => ['prefetch_count' => 5]).

// config/eventbus.php
return [

    'exchanges' => [
        'auth.events',
        'business.events',
    ],

    'queues' => [
        [
            'name' => 'auth_ms.business_events',
            'exchange' => 'business.events',
            'routing_keys' => [
                'tenant.registration.initiated',
            ],
        ],
    ],

    'handlers' => [
        'tenant.registration.initiated' => ProcessTenantCreation::class,
    ],

];

To see every available option: php artisan vendor:publish --tag=eventbus-config.

Every default can also be set per environment with an EVENTBUS_* variable:

Variable Default
EVENTBUS_DLX_ENABLED / EVENTBUS_DLX_TTL / EVENTBUS_MAX_RETRIES true / 86400000 / 3
EVENTBUS_IDEMPOTENCY_ENABLED / _TTL / _LOCK_TTL / _PREFIX true / 3600 / 120 / event:processed:
EVENTBUS_PREFETCH_COUNT / EVENTBUS_HEARTBEAT 1 / 60
EVENTBUS_CONNECTION_TIMEOUT / EVENTBUS_READ_WRITE_TIMEOUT 3 / 130
EVENTBUS_LIVENESS_DIR storage/framework/eventbus
EVENTBUS_PUBLISHER_CONNECTION_TIMEOUT / _READ_WRITE_TIMEOUT / _CONFIRM_TIMEOUT 3 / 15 / 10
EVENTBUS_MONITORING_ENABLED / EVENTBUS_SLOW_EVENT_THRESHOLD true / 5000

Do not bind your own EventProcessor; retries and idempotency are handled by the package.

Publishing Events

use Uzapoint\EventBus\RabbitMQService;

app(RabbitMQService::class)->publish(
    exchange: 'business.events',
    routingKey: 'tenant.registration.initiated',
    data: [
        'auth_user_id' => 12,
        'email' => 'user@example.com',
    ],
    headers: ['idempotency_key' => $key], // optional: used as the event id
);

Envelope:

{
  "meta": {
    "id": "...",
    "type": "tenant.registration.initiated",
    "source": "auth-service",
    "timestamp": "..."
  },
  "data": { ... }
}

Publishing connects with publisher.connection_timeout, read_write_timeout and confirm_timeout, and drops its connection after any failure. A failed publish throws.

Consuming Events

php artisan eventbus:consume
php artisan eventbus:consume --queue=auth_ms.business_events

Run it under supervisor with autorestart=true. Include the shared programs from the package (two consumers plus the watchdog) from the service's supervisord.conf:

[include]
files = /var/www/html/%(ENV_SERVICE_NAME)s/vendor/uzapoint/eventbus-core/ops/supervisor-eventbus.conf

It needs SERVICE_NAME in the container environment and a writable storage/.

Failures

A failing handler is retried dead_letter.max_retries times (2, 4, 6 s backoff), then the message is parked in <queue>.dlq. Alert on any message in a .dlq queue.

Handlers that are queued jobs (ShouldQueue) only enqueue work. Their later failures go to Horizon's failed_jobs, not back to RabbitMQ. Use a plain handler class for critical flows.

Idempotency

'idempotency' => [
    'enabled' => true,
    'ttl' => 3600,      // how long a successfully processed event is remembered
    'lock_ttl' => 120,  // processing lock, released if the handler fails
    'redis_prefix' => 'event:processed:',
],

Namespace the prefix per service (for example event:processed:sales:) when several services consume the same message.

Watchdog

Each consumer touches eventbus-<pid>.alive in consumer.liveness_dir (default storage/framework/eventbus) at least every ~5 s. ops/eventbus-watchdog.sh kills a consumer whose file is older than STALE_AFTER (120 s) and supervisor starts a new one.

Service checklist

  • timeout and read_timeout set on every Redis connection (config/database.php).
  • queue.connections.redis.retry_after greater than the Horizon worker timeout.
  • Job classes assign $this->afterCommit = true; in the constructor and never redeclare the property.

Check all of it, plus handlers, topology, Redis and RabbitMQ:

php artisan eventbus:doctor
php artisan eventbus:doctor --skip-broker

Monitoring

'monitoring' => [
    'enabled' => true,
    'slow_event_threshold' => 5000, // ms
],

License

MIT

Author

Sammy Orondo Lead Developer — Uzapoint https://github.com/sammy-boy