Laravel Events e Listeners — Architettura Event-Driven che Scalano il Fatturato
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Sviluppo di siti web

Laravel Events e Listeners — Architettura Event-Driven che Scalano il Fatturato

[2026-08-08] Author: Ing. Calogero Bono
> condividi
Zenithby Meteora Web Il sistema operativo della tua attività. Social, clienti, prenotazioni e fatture in un'unica piattaforma. Palestre, barber, professionisti. Scopri Zenith Demo gratis · senza carta

Il tuo e-commerce va in down ogni volta che un cliente compra? O peggio, l'email di conferma arriva dopo dieci minuti, quando l'utente ha già chiuso la sessione? Se il tuo codice gestisce tutto dentro al controller, stai pagando per un problema che ha un nome preciso: architettura sincrona e accoppiata. Noi, di Meteora Web, lo vediamo ogni giorno nei progetti che ci arrivano. E la soluzione, in Laravel, si chiama event-driven.

L'idea è semplice: quando succede qualcosa di importante — un ordine, un pagamento, un utente registrato — il tuo sistema non deve fare tutto lì per lì. Deve dire "è successo questo" e lasciare che altre parti del codice reagiscano, in modo indipendente, magari in coda, magari in tempo reale. In questa guida vediamo come impostare Laravel Events, Listeners e Broadcasting per costruire applicazioni che non si bloccano, non perdono dati e non fanno aspettare i clienti.

Perché l'architettura event-driven cambia le regole del gioco?

Pensa a un negozio fisico. Quando un cliente compra, il cassiere non corre a riordinare il magazzino prima di incassare. Registra la vendita, e altre persone — magazziniere, contabile, marketing — reagiscono all'evento "vendita registrata" ognuno per conto proprio. Se il magazziniere è occupato, la vendita non si blocca. Ecco, un'applicazione dovrebbe funzionare così.

In Laravel, gli Events sono classi che rappresentano qualcosa che è successo. I Listeners sono classi che reagiscono a quegli eventi. Il controller, o il servizio, emette l'evento e non sa (e non gli importa) chi lo ascolta. Questo disaccoppiamento porta tre vantaggi concreti:

  • Performance: le operazioni pesanti (email, notifiche, aggiornamenti) possono andare in coda, senza rallentare la risposta al cliente.
  • Manutenibilità: aggiungere una nuova reazione (es. "invia coupon di benvenuto") non tocca il codice dell'ordine. Aggiungi un listener e basta.
  • Affidabilità: se un listener fallisce, l'evento è già stato emesso. Puoi ritentare, loggare, compensare. Il dato principale non si perde.

Noi ragioniamo sempre in termini di costi e ritorni. Un'architettura event-driven riduce il tempo di sviluppo delle nuove feature e i costi di manutenzione. È un investimento che si ripaga, non un vezzo da architetti.

Sponsored Protocol

Come funziona un evento in Laravel?

Un evento è una classe che contiene i dati rilevanti. Per esempio, per un ordine:

// app/Events/OrderCreated.php
namespace App\Events;

use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderCreated
{
    use Dispatchable, SerializesModels;

    public $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }
}

Il listener, invece, è la classe che fa qualcosa con quei dati:

// app/Listeners/SendOrderConfirmation.php
namespace App\Listeners;

use App\Events\OrderCreated;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
use App\Mail\OrderConfirmation;

class SendOrderConfirmation implements ShouldQueue
{
    public function handle(OrderCreated $event)
    {
        Mail::to($event->order->customer_email)
            ->send(new OrderConfirmation($event->order));
    }
}

Il ShouldQueue è la chiave: manda il listener in coda, così la risposta HTTP non aspetta l'email. Il cliente riceve la conferma in pochi secondi, ma il server non è bloccato.

Per registrare la coppia evento-listener, usi il provider EventServiceProvider:

// app/Providers/EventServiceProvider.php
protected $listen = [
    OrderCreated::class => [
        SendOrderConfirmation::class,
        UpdateInventory::class,
        NotifyAdmin::class,
    ],
];

E per emettere l'evento, dal tuo controller o servizio:

use App\Events\OrderCreated;

OrderCreated::dispatch($order);

Ecco fatto. Il controller non sa nulla di email, inventario o notifiche. Ha fatto il suo lavoro: ha registrato l'ordine e ha detto "è successo". Tutto il resto è reattivo.

Sponsored Protocol

Come gestire gli eventi in coda senza perdere dati?

La coda è il cuore dell'event-driven in produzione. Senza coda, l'evento è solo un'astrazione. Con la coda, diventa un sistema che scala. Ma attenzione: una coda mal configurata è peggio di nessuna coda. Ecco i punti critici.

Configurare la coda e i worker

In .env, imposti la connessione. Noi consigliamo Redis o database per iniziare, RabbitMQ o SQS quando il volume cresce.

QUEUE_CONNECTION=redis

Poi avvii il worker:

php artisan queue:work

In produzione, con Supervisor per tenerlo vivo. Se il worker muore, gli eventi restano in coda e vengono processati al riavvio. Nessun dato perso — se hai configurato i retry.

Retry e failure: quando il listener sbaglia

Un listener può fallire: il servizio email è giù, un campo è nullo, una dipendenza non risponde. Laravel ti permette di definire quanti tentativi e con quale backoff:

public $tries = 5;
public $backoff = [10, 30, 60];

E in failed() puoi loggare o notificare. Il punto è: l'ordine è salvo, l'evento è stato emesso. Il fallimento è locale, non sistemico. E con php artisan queue:retry all ripeti i job falliti dopo aver sistemato il problema.

Errori comuni da evitare

  • Non mettere troppa logica nel listener: se un listener fa troppe cose, torna l'accoppiamento. Dividi in listener più piccoli.
  • Ignorare i job falliti: controlla la tabella failed_jobs regolarmente. Un listener che fallisce in silenzio è un bug nascosto.
  • Usare la coda senza testare: in locale, con QUEUE_CONNECTION=sync, i listener girano subito. In produzione, con coda, testa con un worker attivo. La differenza è abissale.

Broadcasting: quando l'evento deve arrivare in tempo reale al browser

Finora abbiamo parlato di reazioni interne al server. Ma ci sono eventi che devono arrivare al cliente in tempo reale: una notifica, un aggiornamento di stato, un messaggio. Laravel Broadcasting fa esattamente questo: trasmette l'evento via WebSocket a chi è connesso.

Sponsored Protocol

Come funziona il broadcasting con Laravel Reverb o Pusher

Laravel 11 ha introdotto Reverb, un server WebSocket nativo, che puoi eseguire con php artisan reverb:start. In alternativa, Pusher è un servizio cloud. La configurazione è simile:

BROADCAST_CONNECTION=reverb
REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
REVERB_HOST=127.0.0.1
REVERB_PORT=8080

Poi crei un evento broadcastabile:

// app/Events/OrderStatusChanged.php
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;

class OrderStatusChanged implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets;

    public $orderId;
    public $status;

    public function __construct($orderId, $status)
    {
        $this->orderId = $orderId;
        $this->status = $status;
    }

    public function broadcastOn()
    {
        return new Channel('orders.' . $this->orderId);
    }

    public function broadcastAs()
    {
        return 'status.updated';
    }
}

E nel frontend, con Echo:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-app-key',
    wsHost: window.location.hostname,
    wsPort: 8080,
    forceTLS: false,
    enabledTransports: ['ws', 'wss'],
});

Echo.channel('orders.123')
    .listen('.status.updated', (e) => {
        console.log('Stato ordine aggiornato:', e.status);
        // Aggiorna la UI senza ricaricare la pagina
    });

Il risultato? Il cliente vede lo stato dell'ordine cambiare in tempo reale, senza refresh. Per un e-commerce, questa è l'esperienza che fa la differenza tra un acquisto e un abbandono.

Canali pubblici e privati

Non trasmettere dati sensibili su canali pubblici. Per notifiche personali, usa PrivateChannel e definisci l'autorizzazione in routes/channels.php:

Sponsored Protocol

Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
    return $user->id === Order::find($orderId)->user_id;
});

Questo garantisce che solo il proprietario dell'ordine riceva l'evento. Una falla qui sarebbe un disastro di sicurezza.

Dove mettere la logica di business: eventi, listener o servizi?

Questa è la domanda che ci fanno sempre. La risposta è: dipende. Ma c'è una regola pratica che usiamo noi:

  • Evento: rappresenta un fatto compiuto. Non contiene logica, solo dati.
  • Listener: reagisce al fatto. Può chiamare un servizio, ma non dovrebbe contenere logica di business complessa.
  • Servizio: contiene la logica di business. Se un'azione ha più di tre passaggi, mettila in un servizio e chiamala dal listener.

Per esempio, il listener UpdateInventory non deve calcolare le giacenze. Deve chiamare InventoryService::updateForOrder($order). Questo mantiene il codice testabile e riutilizzabile.

Come testare eventi e listener senza impazzire?

Il testing è dove l'architettura event-driven mostra i suoi muscoli. Laravel ti dà strumenti per fingere gli eventi e verificare che siano emessi, senza eseguire i listener.

use App\Events\OrderCreated;
use Illuminate\Support\Facades\Event;

public function test_order_created_event_is_dispatched()
{
    Event::fake();

    // Esegui l'azione che dovrebbe emettere l'evento
    $this->post('/orders', [...]);

    Event::assertDispatched(OrderCreated::class, function ($event) use ($order) {
        return $event->order->id === $order->id;
    });
}

E per testare un listener in isolamento, puoi usare Bus::fake() se metti il listener in coda, oppure chiamare direttamente il metodo handle() con un evento finto. Il punto è: con gli eventi, il test è deterministico. Non devi aspettare che la coda giri, non devi simulare il servizio email. Verifichi che l'evento sia stato emesso con i dati giusti, e che il listener faccia la cosa giusta quando viene invocato.

Sponsored Protocol

Quali errori di progettazione evitare nell'event-driven?

L'event-driven non è una bacchetta magica. Se lo usi male, crei più problemi di quanti ne risolvi. Ecco gli errori che vediamo più spesso:

  • Eventi troppo generici: un evento UserUpdated che viene ascoltato da dieci listener, ma nessuno sa perché è stato emesso. Meglio eventi specifici: UserEmailChanged, UserPasswordReset.
  • Listener che modificano lo stesso dato: se due listener aggiornano lo stesso campo in momenti diversi, crei race condition. Sincronizza o dividi le responsabilità.
  • Dimenticare la gestione degli errori: un listener che fallisce e non viene ritentato è un bug. Configura sempre tries e backoff.
  • Non documentare gli eventi: in un team, se non sai che OrderCreated esiste e cosa trasporta, riscrivi codice che già c'è. Documenta gli eventi come fanno per le API.

Noi, di Meteora Web, abbiamo costruito piattaforme proprietarie con questa architettura. La differenza si vede quando il traffico cresce: il sistema regge, le code si svuotano, i clienti non aspettano. È la differenza tra un'applicazione che sopravvive e una che scala.

In sintesi

L'architettura event-driven in Laravel non è un lusso per grandi progetti. È una disciplina che ti fa risparmiare tempo, denaro e notti insonni. Ecco le azioni immediate:

  • Identifica le azioni critiche nel tuo dominio (ordini, pagamenti, registrazioni) e trasformale in eventi.
  • Sposta le operazioni lente (email, notifiche, integrazioni) in listener in coda con retry e backoff.
  • Configura il broadcasting per le notifiche in tempo reale, con canali privati e autorizzazioni.
  • Scrivi test con Event::fake() per ogni evento che emetti. Se non è testato, non esiste.
  • Documenta gli eventi per il tuo team. Un evento senza documentazione è un debito tecnico.

Se vuoi approfondire l'intero framework, parti dal nostro pillar su Laravel. E se hai un progetto che soffre di accoppiamento e lentezza, sai dove trovarci.

> condividi
Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()