aiarmada/events
A Laravel package for working with events in a structured, framework-friendly way, helping you organize event definitions and handling within your application.
aiarmada/events is a Laravel package that sits underneath the AIArmada commerce stack (carts, orders, payments, fulfillment). Treat it as the wiring layer that lets those domains announce "something happened" without knowing who cares — not as a general-purpose replacement for Laravel's native event system.
Minimum viable setup
composer require aiarmada/events
php artisan vendor:publish --provider="AIArmada\Events\EventsServiceProvider"
php artisan migrate
Then, before writing a single listener, spend 15 minutes in three places:
config/events.php — this is where the package's opinions live: queue connection, whether events persist, discovery paths, sync-vs-async defaults. Read every key once; you will come back to it.src/EventsServiceProvider.php or similar) — tells you what is auto-registered, what is deferred, and which contracts are bound. This is your map of extension points.tests/ — on a package with zero stars and zero dependents, the test suite is the documentation. It shows the intended happy path far more reliably than a README that may lag behind.First use case: get one side effect out of a controller.
The highest-leverage first move is not "build an event architecture." It is picking one fat controller method — say OrderController@store, which currently charges a card, sends an email, decrements inventory, and pings a warehouse — and moving three of those four responsibilities behind a single OrderPlaced event. You get an immediate readability win, and you learn the package's dispatch and listener-registration conventions on real code instead of a tutorial.
The package earns its keep when events are raised by the thing that owns the state change, not by HTTP handlers. Whether that means model observers, aggregate methods, or explicit Event::dispatch() calls depends on what the package exposes — check the source for the base event class and any trait it ships. The pattern to aim for:
// Inside your order aggregate / service, after the DB write succeeds
OrderPlaced::dispatch($order->id, $order->total, $order->customer_id);
Pass scalar IDs and primitives, not Eloquent models. This is the single most important habit with any event package: serialized models go stale the moment they hit a queue, and a listener that re-fetches by ID always sees current state.
Split listeners along a clear line:
Most commerce bugs come from queuing something that was actually an invariant, or running synchronously something that takes 800ms and shouldn't block checkout.
If the package ships an outbox or persisted-event table (check the migrations you just published), use it for events that leave the application — payment provider callbacks, warehouse notifications, ERP sync. The pattern: write the event row in the same transaction as the state change, then a relay process publishes it. You get at-least-once delivery instead of "we lost the order confirmation because Redis restarted."
At-least-once delivery means your listeners will run twice. Give every listener a dedupe guard keyed on the event's unique identifier (the package almost certainly generates one — verify in the base event class). A processed_events table with a unique index on the event ID is boring, cheap, and saves you from double-charging a customer.
Event::fake([OrderPlaced::class]);
$this->post('/orders', $payload)->assertRedirect();
Event::assertDispatched(OrderPlaced::class, fn ($e) => $e->orderId === $order->id);
Then test the listener in isolation by constructing the event directly and invoking handle(). Do not test the whole chain end-to-end in a unit test — you will spend your life debugging ordering rather than logic.
Adoption risk is the headline. Zero stars, zero dependents, zero score. This is an ecosystem-internal package, and your bet is really on the AIArmada commerce stack surviving and maintaining it. Concretely: pin the exact version in composer.json, read the CHANGELOG before every composer update, and make sure your critical listeners are covered by tests you own. If AIArmada commerce is not already in your stack, this package is not a reason to adopt it.
Dispatch-after-commit races. If you dispatch inside a transaction and the listener is queued, the worker can pick up the job before the transaction commits and read stale or missing rows. Laravel's DB::afterCommit() (or dispatchAfterResponse where appropriate) fixes this — check whether the package does it for you or leaves it to you. This is the most common "works on my machine, flakes in production" failure mode.
Double-firing from observers plus explicit dispatch. If you hook Order::created and your service layer dispatches OrderPlaced, you get two events. Pick one emission point per state change and write it down in your team's conventions doc.
Event versioning. Event classes are a public contract the moment another module listens. Adding a required constructor argument is a breaking change. Prefer adding optional properties, and namespace events by version (OrderPlacedV2) if you expect churn. Since this package is young, expect its contracts to churn too.
Ordering is not guaranteed. Queued listeners on multiple workers will not preserve emission order. If a listener depends on a prior event (e.g., OrderShipped assuming OrderPaid was processed), either collapse them into one event or key your queue so related events land on the same worker.
Config and discovery caching. Run php artisan config:clear and any package-specific discovery cache clear after touching config/events.php or adding listeners. A silently un-discovered listener looks exactly like a broken dispatcher, and you will lose an hour to it.
Debugging checklist, in order: (1) Event::fake() accidentally left in a test, (2) listener not registered/discovered — dump the registered listeners from the container, (3) queue worker running old code after a deploy — queue:restart is not optional, (4) payload serialization failure swallowed by a failed-job table nobody checks.
Extension points worth knowing early: the contracts bound in the service provider. If the package exposes an interface for the dispatcher or the event store, you can swap in your own implementation for testing or for migration to Laravel's native events later — which is your realistic exit strategy if the package stalls.
How can I help you explore Laravel packages today?