micro-module/event-queue
A lightweight Laravel package for queuing and dispatching events asynchronously. Built for micro-module architectures, it decouples event producers from listeners and leverages Laravel’s queue system for reliable background processing, with simple configuration for modular apps.
composer require micro-module/event-queue
php artisan vendor:publish --provider="MicroModule\EventQueue\EventQueueServiceProvider"
php artisan migrate
config/event-queue.php first. That file is the real documentation: it tells you the storage table, the queue connection/name to use, retry counts, and whether processing is driven by Laravel's queue worker or a package-specific command.php artisan event-queue:work) or it pushes standard Laravel jobs — run php artisan list | grep -i event and inspect routes//src/Console if the README is thin.UserRegistered event that writes a row to the package table and is later processed by the worker — confirm the row transitions from pending to processed before wiring in real business logic.Transactional outbox for domain events. The strongest day-to-day use of a DB-backed event queue is writing the event record in the same transaction as the business change, then processing asynchronously. That guarantees you never emit an event for a rolled-back write:
DB::transaction(function () use ($order) {
$order->update(['status' => 'paid']);
EventQueue::push(new OrderPaid($order->id)); // or the package's dispatch helper
});
Keep listeners idempotent (key on the event's stored ID or a natural key) because at-least-once delivery is the norm.
Payload discipline. Store scalars, not Eloquent models. Pass $order->id and re-fetch inside the handler. If the package uses SerializesModels, models still get re-queried and can be deleted mid-flight — IDs make that failure obvious instead of silent.
Handler/hook registration. Most micro-module packages let you map event classes to handlers in config or via a handle method. Register handlers in a dedicated service provider and keep them thin: resolve a domain service, call it, return. Avoid DB writes in the handler that duplicate the outbox row.
Ordering per aggregate. If you need per-order or per-user ordering, route events to a queue name derived from the aggregate key (e.g. orders-{shard}) or process with a single worker where ordering matters. Don't assume global ordering across workers.
Retries and dead-lettering. Point the package's queue at a dedicated connection (event-queue connection with its own Redis/DB queue) so event work doesn't starve user-facing jobs. Set tries/backoff in the config, and monitor the package's status column or failed-jobs table — whichever it exposes — rather than relying on queue:failed alone.
Integration with Laravel events. If you already dispatch Laravel events, prefer adding ShouldQueue to the event and using $event->afterCommit() for the simple cases; reserve this package for when you need persistence, replay, or an audit trail of what was emitted. Don't route the same event through both mechanisms.
ShouldQueue to an event and pushing it through the package will process it twice. Pick one path per event class and document it in the event's docblock.afterCommit/dispatchAfterCommit option; if it doesn't exist, push after the transaction closes.php artisan config:cache in deploy will freeze the table/queue names. Run config:clear before config:cache in your deploy script, and restart workers after every deploy (queue:restart).Queue::fake() won't cover a package-managed table — assert on the DB row instead.updateOrCreate, unique constraints, or a processed-event ledger keyed by the stored event ID.composer.json, and treat the package's src/ as the source of truth once installed. If you need behaviour the config can't express, the usual extension points are a custom event model (subclass the package's model and rebind it in a service provider), a custom handler resolver, or wrapping the dispatcher in your own service so you can swap the package later without touching call sites.How can I help you explore Laravel packages today?