---
title: "Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili"
description: "L'event discovery, introdotto in Laravel 5.8.9 e default da Laravel 11, elimina la registrazione manuale nell'EventServiceProvider. I listener si registrano per type-hint."
canonical: "https://www.mauriziofonte.it/blog/post/modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.html"
author: "Maurizio Fonte"
date: "2025-02-26T10:37:38+01:00"
modified: "2026-04-14T17:48:55+02:00"
section: "Laravel"
tags: ["Laravel", "Refactoring"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Laravel](https://www.mauriziofonte.it/blog/cat/laravel.html) [Refactoring](https://www.mauriziofonte.it/blog/cat/refactoring.html)

# [Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili](https://www.mauriziofonte.it/blog/post/modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.html)

Mercoledì 26 Febbraio 2025, alle 10:37 ![Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili](https://www.mauriziofonte.it/image/001440200-modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.jpg)

In una piattaforma marketplace con migliaia di utenti attivi, l'EventServiceProvider conteneva 67 mapping evento→listener - un file da 180 righe dove ogni modifica richiedeva di scorrere l'intero array `$listen` per trovare la posizione corretta. Il [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) definisce l'Observer pattern come "una dipendenza one-to-many tra oggetti, dove un cambiamento di stato notifica automaticamente tutti i dipendenti." Laravel implementa questo pattern con eventi e listener - ma fino a Laravel 10, la registrazione manuale nell'EventServiceProvider era il collo di bottiglia organizzativo.

## Come funziona l'event discovery in Laravel 12 e cosa sostituisce?

L'[event discovery](https://laravel.com/docs/12.x/events), introdotto in Laravel 5.8.9 (aprile 2019) come opt-in e diventato il default da Laravel 11, elimina la registrazione manuale. Il framework scansiona la directory `app/Listeners`, riflette ogni classe pubblica e ispeziona i metodi `handle()` e `__invoke()`: il type-hint del parametro determina quale evento il listener gestisce. In produzione, `php artisan event:cache` genera un manifest cachato per evitare la scansione a ogni request.

Il cambiamento strutturale più significativo è avvenuto con Laravel 11 (marzo 2024): l'EventServiceProvider è stato [rimosso dallo skeleton di default](https://github.com/laravel/laravel/pull/6172/) - insieme ad altri ~69 file nella nuova struttura applicativa snella. La registrazione avviene interamente per auto-discovery, con la possibilità di aggiungere listener manuali via `Event::listen()` in `AppServiceProvider`:

```php
/* Listener auto-discovered - nessuna registrazione manuale necessaria */
namespace App\Listeners;

use App\Events\OrderPlaced;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
use App\Mail\OrderConfirmationMailable;

class SendOrderConfirmation implements ShouldQueue
{
    /* Il type-hint su OrderPlaced è sufficiente per la registrazione automatica.
     * ShouldQueue rende il listener asincrono - eseguito da un worker. */
    public function handle(OrderPlaced $event): void
    {
        Mail::to($event->order->customer_email)
            ->send(new OrderConfirmationMailable($event->order));
    }
}

/* In Laravel 12, per directory custom di listener: */
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withEvents(discover: [
        __DIR__.'/../app/Listeners',
        __DIR__.'/../app/Domain/Orders/Listeners',
    ])
    ->create();

/* Registrazione manuale (solo se necessaria) in AppServiceProvider */
public function boot(): void
{
    Event::listen(OrderPlaced::class, function (OrderPlaced $event) {
        Log::info("Ordine {$event->order->id} piazzato");
    });
}
```

Martin Fowler [identifica](https://martinfowler.com/articles/201701-event-driven.html) quattro pattern distinti sotto il termine "event-driven": Event Notification, Event-Carried State Transfer, Event Sourcing e CQRS. L'event system di Laravel implementa il primo - Event Notification - dove il producer segnala un fatto accaduto e i consumer reagiscono indipendentemente. [AWS documenta](https://aws.amazon.com/event-driven-architecture/) che questo pattern garantisce disaccoppiamento, scalabilità indipendente e fault tolerance - esattamente i vantaggi che l'auto-discovery amplifica eliminando l'accoppiamento configurazionale dell'EventServiceProvider.

## Come testare che gli eventi vengano dispatchati e i listener reagiscano correttamente?

`Event::fake()` intercetta il dispatch degli eventi senza eseguire i listener - verifica il "cosa viene emesso". Per testare il listener stesso, si istanzia direttamente e si chiama `handle()`, come per i [job in coda](https://www.mauriziofonte.it/blog/post/modernizzare-job-coda-laravel-testing-queue-fake-interactions-l12.html) con `withFakeQueueInteractions()`:

```php
/* Test di integrazione: l'evento viene dispatchato? */
public function test_order_creation_dispatches_event(): void
{
    Event::fake([OrderPlaced::class]);

    $order = Order::factory()->create();
    app(OrderService::class)->confirm($order);

    Event::assertDispatched(OrderPlaced::class, fn ($e) =>
        $e->order->id === $order->id
    );
    Event::assertDispatchedTimes(OrderPlaced::class, 1);
}

/* Test unitario del listener: la logica interna funziona? */
public function test_send_order_confirmation_sends_email(): void
{
    Mail::fake();

    $order = Order::factory()->create();
    $listener = new SendOrderConfirmation();
    $listener->handle(new OrderPlaced($order));

    Mail::assertSent(OrderConfirmationMailable::class, fn ($mail) =>
        $mail->hasTo($order->customer_email)
    );
}
```

La combinazione dei due livelli - dispatch e logica - garantisce copertura completa senza dipendere dall'auto-discovery nei test. I [test automatici](https://www.mauriziofonte.it/blog/post/test-automatici-codebase-php-legacy-senza-riscrittura.html) dei listener girano in millisecondi perché non c'è overhead di discovery.

## Errori comuni nella gestione degli eventi Laravel

Il primo errore è confondere l'auto-discovery di Laravel con l'attributo `#[AsEventListener]` di Symfony. L'attributo [`AsEventListener`](https://symfony.com/doc/current/event_dispatcher.html), introdotto in Symfony 5.3, è un meccanismo del Symfony EventDispatcher - non funziona in Laravel. Laravel usa esclusivamente il type-hint del parametro `handle()` per determinare quale evento un listener gestisce. Importare `Symfony\Component\EventDispatcher\Attribute\AsEventListener` in un listener Laravel non ha alcun effetto.

Il secondo è mantenere un EventServiceProvider custom durante l'upgrade da Laravel 10 a 11 senza disabilitare l'auto-discovery. Il framework [registra l'auto-discovery di default](https://github.com/laravel/framework/issues/52714) - se il vecchio EventServiceProvider rimane attivo con `$listen`, i listener vengono registrati due volte (una via `$listen`, una via discovery), causando esecuzioni duplicate.

Il terzo è usare `singleton()` per listener che accedono a stato mutabile. Un listener registrato come singleton mantiene lo stato tra eventi diversi nella stessa request - se modifica proprietà interne durante `handle()`, l'evento successivo riceve stato inquinato. I listener devono essere stateless o registrati con `bind()` nel [Service Container](https://www.mauriziofonte.it/blog/post/flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.html).

Il quarto è non cachare gli eventi in produzione. Senza `event:cache`, Laravel esegue la scansione delle directory e la reflection a ogni request - un overhead misurabile su applicazioni con decine di listener. Il comando `php artisan event:cache` genera il manifest; `event:clear` lo invalida durante il deploy.

L'event system di Laravel è il meccanismo fondamentale per il disaccoppiamento - ogni azione di business significativa (ordine piazzato, pagamento ricevuto, utente registrato) dovrebbe produrre un evento che i listener consumano indipendentemente. La [struttura snella di Laravel 12](https://www.mauriziofonte.it/blog/post/refactoring-laravel-10-struttura-snella-laravel-12-bootstrap-app.html) rende l'auto-discovery la scelta naturale, e i [middleware avanzati](https://www.mauriziofonte.it/blog/post/laravel-12-middleware-avanzati-sicurezza-logica-business-pmi-refactoring.html) completano il quadro per applicativi modulari. Per conoscere il mio approccio all'architettura event-driven in Laravel, visita [la mia pagina professionale](https://www.mauriziofonte.it/pages/about). Se il tuo EventServiceProvider è un file da centinaia di righe con mapping manuali che crescono a ogni feature, [contattami per una consulenza dedicata](https://www.mauriziofonte.it/pages/contattami) - partiamo dall'inventario degli eventi e dalla migrazione all'auto-discovery.

Dall'articolo al lavoro vero

Se il codice di cui parla questo articolo è il tuo

Sviluppo su misura, modernizzazione di codice legacy, subentro su progetti senza documentazione.

[Vedi tre interventi con i numeri](https://www.mauriziofonte.it/programmatore-php.html) Ultima modifica: Martedì 14 Aprile 2026, alle 17:48 [« Articolo precedenteUUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB25 Febbraio 2025](https://www.mauriziofonte.it/blog/post/refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.html)[Articolo successivo »Sicurezza upload immagini in Laravel 12: perché la regola image esclude gli SVG e come validare i file in modo sicuro27 Febbraio 2025](https://www.mauriziofonte.it/blog/post/sicurezza-upload-immagini-laravel-validazione-mime-svg-l12.html)

### Articoli correlati a Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili

- [Health check applicativi Laravel 12: da controller custom a Health Routing con DiagnosingHealth e spatie/laravel-health](https://www.mauriziofonte.it/blog/post/monitoraggio-proattivo-laravel-health-routing-l12-refactoring.html)

  L'Health Routing di Laravel, introdotto in Laravel 11 PR 47309 , espone un endpoint /up che dispatcha l'evento DiagnosingHealth i listener che · 10 Marzo 2025

  ![Health check applicativi Laravel 12: da controller custom a Health Routing con DiagnosingHealth e spatie/laravel-health](https://www.mauriziofonte.it/image/001510070-monitoraggio-proattivo-laravel-health-routing-l12-refactoring.jpg)
- [Event-driven architecture con PHP: dall'evento al handler senza accoppiamento](https://www.mauriziofonte.it/blog/post/event-driven-architecture-php-disaccoppiamento-handler.html)

  Un gestionale ordini con 14 side effect per ogni conferma d'ordine: email, aggiornamento magazzino, contabilità, notifiche. Tutto in un controller. · 3 Dicembre 2025

  ![Event-driven architecture con PHP: dall'evento al handler senza accoppiamento](https://www.mauriziofonte.it/image/003350070-event-driven-architecture-php-disaccoppiamento-handler.jpg)
- [Fat Controller in Laravel 12: dal controller da 200 righe a Service Layer, Action pattern e Dependency Injection](https://www.mauriziofonte.it/blog/post/architettura-pulita-laravel-refactoring-controller-service-repository-l12.html)

  Robert C. Martin definisce il Single Responsibility Principle come "un modulo deve essere responsabile verso un solo attore". Un controller Laravel · 11 Marzo 2025

  ![Fat Controller in Laravel 12: dal controller da 200 righe a Service Layer, Action pattern e Dependency Injection](https://www.mauriziofonte.it/image/001520070-architettura-pulita-laravel-refactoring-controller-service-repository-l12.jpg)
- [Refactoring applicazione Laravel 10: guida passo-passo per adottare la struttura snella di Laravel 12 e centralizzare la configurazione](https://www.mauriziofonte.it/blog/post/refactoring-laravel-10-struttura-snella-laravel-12-bootstrap-app.html)

  Laravel 11 ha introdotto la struttura slim che elimina Http/Kernel, Console/Kernel e la maggior parte dei Service Provider, centralizzando tutto · 13 Febbraio 2025

  ![Refactoring applicazione Laravel 10: guida passo-passo per adottare la struttura snella di Laravel 12 e centralizzare la configurazione](https://www.mauriziofonte.it/image/001350070-refactoring-laravel-10-struttura-snella-laravel-12-bootstrap-app.jpg)
- [Testing dei job in coda Laravel: da Queue::fake() a withFakeQueueInteractions() per validare retry, release e failure senza broker](https://www.mauriziofonte.it/blog/post/modernizzare-job-coda-laravel-testing-queue-fake-interactions-l12.html)

  Queue::fake verifica che un job venga dispatchato correttamente, ma non testa cosa succede dentro handle quando il job deve rilasciarsi, cancellarsi · 19 Febbraio 2025

  ![Testing dei job in coda Laravel: da Queue::fake() a withFakeQueueInteractions() per validare retry, release e failure senza broker](https://www.mauriziofonte.it/image/001390070-modernizzare-job-coda-laravel-testing-queue-fake-interactions-l12.jpg)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili", mauriziofonte.it, 2025. https://www.mauriziofonte.it/blog/post/modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.html

Pubblicato: 2025-02-26 · Aggiornato: 2026-04-14.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
