Il problema dell'effetto domino
Quando salvi un articolo, devi invalidare la cache. Quando cancelli un utente, devi eliminare le sue sessioni. Quando aggiorni un prezzo, devi ricalcolare i totali. Ogni azione scatena reazioni — e la tentazione e infilare tutto nel metodo save() del modello, creando un metodo che fa dieci cose e ne conosce altrettante. E una violazione flagrante del Single Responsibility Principle: il modello dovrebbe occuparsi di persistenza, non di cache, notifiche e audit.
L'Observer Pattern rompe questa catena causale diretta. Il modello non sa chi lo osserva e non gli interessa: sa solo che quando cambia il suo stato, deve notificare chi si e registrato. L'invalidazione della cache, il log dell'audit trail, l'invio della notifica — sono tutti osservatori che vivono altrove, disaccoppiati dal modello che li attiva. Ogni osservatore ha una sola responsabilita, e il modello recupera la sua.
Cos'e l'Observer Pattern: definizione e struttura
Il Gang of Four definisce l'Observer come un pattern comportamentale che "definisce una dipendenza uno-a-molti tra oggetti, in modo che quando un oggetto cambia stato, tutti i suoi dipendenti vengano notificati e aggiornati automaticamente". Gli attori sono: il Subject (l'oggetto osservato), l'Observer (l'interfaccia per gli osservatori), e i ConcreteObserver (le implementazioni che reagiscono ai cambiamenti).
Il Subject mantiene una lista di Observer registrati e offre metodi per aggiungere e rimuovere osservatori. Quando il suo stato cambia, itera sulla lista e chiama il metodo di notifica di ogni osservatore. La chiave e l'inversione della dipendenza: il Subject non dipende dai ConcreteObserver ma dall'interfaccia Observer. Aggiungere un nuovo osservatore non richiede di modificare il Subject — solo di registrare una nuova implementazione.
Il lifecycle dei modelli in Soft PHP MVC
Ogni modello nel framework attraversa un lifecycle con hook definiti: beforeSave, afterSave, beforeDelete, afterDelete. Questi hook non sono eventi nel senso classico di un event dispatcher — sono metodi template che le sottoclassi possono sovrascrivere. E una variante dell'Observer piu vicina al Template Method, dove il framework definisce lo scheletro e il modello concreto riempie i dettagli.
Il QueryCacheObserver ne e un esempio pratico: quando un modello con il trait HasQueryCache viene salvato o eliminato, la cache delle query correlate viene invalidata automaticamente. Il modello non chiama Cache::forget() direttamente — il trait registra l'osservatore, e l'osservatore gestisce la pulizia. Se domani la strategia di cache cambia (da file a database, da database a Redis), il modello non ne risente.
Come funziona l'invalidazione della cache
L'invalidazione avviene in modo granulare: non si svuota tutta la cache, ma solo le chiavi relative al modello modificato. Il QueryCacheObserver conosce il prefisso della cache per ogni modello e invalida solo le entry pertinenti. Questo approccio bilancia performance e correttezza: la cache resta calda per i dati non modificati, ma si aggiorna immediatamente per quelli cambiati. Il pattern Observer rende questa logica trasparente — il codice del controller non sa nemmeno che la cache esiste.
Publish-Subscribe vs Observer classico
L'Observer del GoF prevede un legame diretto: il subject mantiene una lista di observer e li notifica. Il Publish-Subscribe aggiunge un intermediario — il message broker, l'event bus — che disaccoppia ulteriormente. Nel Pub/Sub, il publisher non conosce i subscriber e i subscriber non conoscono il publisher: comunicano attraverso topic o canali. In Soft PHP MVC, il middleware stack funziona come un bus implicito: ogni middleware "osserva" la request senza conoscere gli altri middleware nella catena.
Il VisitorTrackingMiddleware osserva ogni richiesta e decide se tracciare il visitatore. Il RateLimitMiddleware osserva le stesse richieste e decide se bloccarle. Nessuno dei due sa dell'esistenza dell'altro. E questa ignoranza reciproca e una virtu, non un difetto: meno un componente sa degli altri, meno motivi ha per cambiare quando gli altri cambiano. E il principio dell'accoppiamento minimo nella sua forma piu pratica.
Observer sincrono vs asincrono
Nel framework, gli observer vengono eseguiti in modo sincrono — nel contesto della stessa richiesta HTTP. Questo semplifica il debugging (il flusso e prevedibile) ma introduce un trade-off: se un observer e lento, rallenta tutto. In un framework piu complesso, gli observer asincroni — basati su code (queue) — risolvono il problema delegando l'esecuzione a un worker separato. Per Soft PHP MVC, il sincrono e la scelta pragmatica giusta: il framework e leggero, e la complessita delle code non si ripaga su un progetto di questa scala.
Applicazioni pratiche dell'Observer in PHP
L'Observer Pattern trova applicazione in numerosi scenari concreti nello sviluppo PHP:
- Invalidazione cache — svuotare la cache quando i dati sottostanti cambiano
- Audit trail — registrare ogni modifica a un modello per compliance o debugging
- Notifiche — inviare email o push notification quando si verifica un evento significativo
- Aggiornamento indici di ricerca — risincronizzare un motore di ricerca quando il contenuto cambia
- Metriche e analytics — tracciare eventi di business senza inquinare la logica di dominio
In ogni caso, il principio e lo stesso: il codice che produce l'evento non sa (e non deve sapere) chi lo consuma. L'architettura ad eventi emerge naturalmente quando si applica l'Observer con disciplina, separando la logica di dominio dalle reazioni collaterali.
Il costo dell'indirezione
L'Observer non e gratis. Ogni livello di indirezione aggiunge complessita al debugging: quando qualcosa va storto, il flusso non e piu una linea retta ma un grafo di notifiche. "Chi ha invalidato questa cache?" diventa una domanda non banale se cinque osservatori ascoltano lo stesso evento. Il codice perde la proprieta di localita — per capire cosa succede quando un modello viene salvato, devi sapere chi lo osserva, e quegli osservatori possono essere registrati ovunque.
La regola pratica e semplice: se hai un solo reagente, una chiamata diretta e piu chiara di un evento. Se ne hai due, valuta. Se ne hai tre o piu, l'Observer si ripaga. Come ogni design pattern, non e una legge universale ma un trade-off consapevole — meno accoppiamento in cambio di meno tracciabilita. Il segreto e sapere quando il trade-off conviene, e nel framework Soft PHP MVC, ogni uso dell'Observer e stato valutato con questo criterio pragmatico.