Laravel Mastery: Event-Driven Architecture

11 minute read Laravel Mastery · Part 4

Master Laravel's event system to decouple logic, improve testability, and scale your application with event listeners, subscribers, and broadcasting.

Laravel’s event system allows you to build highly decoupled applications where logic can be neatly separated, extended, and scaled. This approach shines when triggering multiple reactions from a single action - like sending emails, logging activity, and queuing background jobs after a purchase.

Why Use Events?

Event-driven design:

  • Decouples logic – keeps core actions lean
  • Improves testability – listeners can be tested in isolation
  • Enhances scalability – listeners can run synchronously or via queues
  • Encourages SRP – each listener handles a single job

Basic Event + Listener

Generate both:

php artisan make:event OrderPlaced
php artisan make:listener SendOrderConfirmation --event=OrderPlaced

Event

<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderPlaced implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(public Order $order)
    {
    }
}

Listener

class SendOrderConfirmation
{
    public function handle(OrderPlaced $event)
    {
        Mail::to($event->order->user)->send(new OrderConfirmation($event->order));
    }
}

Register in EventServiceProvider.php:

protected $listen = [
    OrderPlaced::class => [
        SendOrderConfirmation::class,
        LogOrderActivity::class,
        QueueAnalyticsEvent::class,
    ],
];

Dispatching Events

Anywhere in your app:

OrderPlaced::dispatch($order);

Or use the helper:

event(new OrderPlaced($order));

Listener Queues for Performance

Use queued listeners:

class QueueAnalyticsEvent implements ShouldQueue
{
    public function handle(OrderPlaced $event)
    {
        Analytics::track('order_placed', [
            'order_id' => $event->order->id,
            'user_id' => $event->order->user_id,
        ]);
    }
}

Queue config lives in config/queue.php. Monitor it with Horizon.

Event Subscribers for Clean Grouping

Subscribers let you group multiple listeners in one class:

class UserEventSubscriber
{
    public function onLogin($event)
    {
        // log or notify
    }

    public function onLogout($event)
    {
        // track session end
    }

    public function subscribe(Dispatcher $events)
    {
        $events->listen(Login::class, [self::class, 'onLogin']);
        $events->listen(Logout::class, [self::class, 'onLogout']);
    }
}

Register in EventServiceProvider.php:

protected $subscribe = [
    UserEventSubscriber::class,
];

Broadcasting Events

Broadcast events via Pusher, Ably, or Redis:

class OrderPlaced implements ShouldBroadcast
{
    public function broadcastOn()
    {
        return new PrivateChannel('orders.'.$this->order->id);
    }
}

Enable broadcasting in config/broadcasting.php, set up a frontend listener with Laravel Echo:

Echo.private(`orders.${orderId}`)
    .listen('OrderPlaced', (e) => {
        console.log('New order broadcast received', e);
    });

Best Practices

  • Use event names that reflect domain actions (e.g. OrderShipped, InvoiceGenerated)
  • Keep listeners focused and lean
  • Queue heavy work like notifications, analytics, or third-party calls
  • Test events and listeners in isolation
  • Monitor your queues with Horizon or Telescope

Real-World Application

In the high-performance Laravel ecommerce project, we used events to:

  • Queue order processing steps
  • Broadcast inventory updates
  • Log user activity asynchronously