L’architettura orientata agli eventi è uno stile di progettazione in cui i servizi annunciano fatti immutabili come eventi e i consumatori reagiscono da soli. La precisione che le pagine dei fornitori saltano sta in due parole: un evento è un fatto immutabile, al passato – OrderPlaced, non PlaceOrder – e il produttore non sa chi reagisce, né gli interessa. Quell’indifferenza è l’architettura: i consumatori si aggiungono, si rimuovono e si rompono senza che il produttore cambi una riga, ed è il disaccoppiamento nel senso più letterale. E l’EDA è uno spettro, non una religione – un singolo webhook è orientato agli eventi; lo è anche una piattaforma di streaming; la maggior parte dei sistemi sani è ibrida.
Punti chiave
| Domanda | Risposta |
|---|---|
| Un evento è | Un fatto immutabile, al passato – i consumatori possono reagire, mai rifiutare |
| Evento ≠ comando | Fatti diffusi a chiunque sia interessato vs. imperativi rivolti a un solo handler |
| La regola decisionale | Lavoro del produttore concluso prima delle reazioni → eventi; chi chiama ha bisogno della risposta → richiesta-risposta |
| Le tre varianti | Notification · event-carried state · event sourcing (una scelta di archiviazione) |
| Il conto fisso | Consumatori idempotenti, ID di correlazione, UX pensata per l’eventual consistency |
Un fatto, molte reazioni
// JavaScript — Cloud Code (cloud/main.js)
// Everyday EDA: the write IS the event; consumers react independently
Parse.Cloud.afterSave('Order', async (req) => {
const order = req.object;
const becamePlaced = order.get('status') === 'placed' &&
req.original?.get('status') !== 'placed';
if (!becamePlaced) return;
// Consumers of one fact — none knows about the others:
await Parse.Cloud.httpRequest({ // fulfillment system (webhook)
method: 'POST', url: process.env.FULFILL_WEBHOOK,
body: { event: 'order.placed', id: order.id },
});
await enqueueConfirmationEmail(order); // async job queue
// Live Query pushes the change to every open dashboard automatically
}); // Flutter / Dart — Back4app Flutter SDK
// The client as event consumer: subscribe to facts, react on arrival
final orders = QueryBuilder<ParseObject>(ParseObject('Order'))
..whereEqualTo('status', 'placed');
final sub = await LiveQuery().client.subscribe(orders);
sub.on(LiveQueryEvent.create, (order) => showNewOrder(order));
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. // iOS / Swift — Back4app Swift SDK
// The client as event consumer: subscribe to facts, react on arrival
let orders = Order.query("status" == "placed")
let sub = try await orders.subscribe()
sub.handleEvent { _, event in
if case .created(let order) = event { showNewOrder(order) }
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. // Android / Kotlin — Back4app Android SDK
// The client as event consumer: subscribe to facts, react on arrival
val orders = ParseQuery.getQuery<ParseObject>("Order")
orders.whereEqualTo("status", "placed")
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(orders)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, order ->
showNewOrder(order)
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. Un fatto – un ordine è stato effettuato – e quattro consumatori, nessuno dei quali conosce gli altri: un sistema di evasione avvisato per webhook, un job di e-mail messo in coda, l’analytics che conta, le dashboard aggiornate da una sottoscrizione in diretta. Aggiungere un quinto consumatore non cambia nulla a monte. Tutto l’argomento sta in un solo salvataggio.
Il vocabolario, districato
Il caos terminologico dei risultati di ricerca, risolto in una tabella – tre tipi di messaggio classificati per intenzione, non per trasporto:
| Comando | Evento | Query | |
|---|---|---|---|
| Grammatica | Imperativo: ShipOrder | Passato: OrderShipped | Interrogativo |
| Destinatari | Un solo handler | Chiunque abbia sottoscritto | Un solo rispondente |
| Si può rifiutare | Sì | No – è già accaduto | n/d |
| Accoppiamento | Il mittente conosce l’handler | Il produttore non conosce nessuno | Chi chiama attende |
L’avvertimento di Fowler merita di essere citato: attenzione al comando passivo-aggressivo – un “evento” che il produttore emette pretendendo in segreto l’azione di un consumatore preciso. Se il flusso si rompe quando quel consumatore non risponde, era un comando dall’inizio, e chiamarlo evento ha solo nascosto l’accoppiamento. (Le parole dell’infrastruttura – broker, router, bus, canale, topic – sono quasi sinonimi per la casella centrale; la voce sul pub/sub copre quella meccanica, di cui l’EDA è l’architettura che generalizza.)
Orientato agli eventi vs. richiesta-risposta
| Richiesta-risposta | Orientato agli eventi | |
|---|---|---|
| Accoppiamento temporale | Chi chiama attende – il destinatario deve essere attivo | Fire-and-forget – i consumatori recuperano il ritardo |
| La risposta | Restituita a chi chiama | Non c’è risposta, solo reazioni |
| Modalità di guasto | Errore immediato e visibile | Resiliente – e con eventual consistency |
| Debug | Un unico stack di chiamate | ID di correlazione attraverso i salti |
| Adatto a | Letture, accessi, pagamenti – tutto ciò che a chi chiama serve | Fatti con consumatori indipendenti |
La regola in una frase: se chi chiama ha bisogno del risultato per proseguire, richiesta-risposta; se il lavoro del produttore è concluso prima che inizi qualsiasi reazione, eventi. Effettuare l’ordine esige una risposta sincrona (“è andato a buon fine?”); tutto ciò che viene dopo – e-mail, evasione, analytics – sono reazioni a un fatto, ed è per questo che i sistemi reali sono ibridi per progetto e non per compromesso.
Le tre varianti di Fowler – e dove si colloca davvero l’event sourcing
| Pattern | L’evento porta | Consumatori | Trade-off |
|---|---|---|---|
| Event notification | Un ID e poco altro | Richiamano per i dettagli | Semplice; il traffico di richiamo riaccoppia |
| Event-carried state transfer | Tutto lo stato rilevante | Conservano copie locali | Nessun richiamo; duplicazione e dati obsoleti |
| Event sourcing | È il sistema di riferimento | Ricostruiscono lo stato con un replay | Audit perfetto; un impegno di archiviazione pesante |
La distinzione che risparmia riunioni: l’event sourcing è il modo in cui un sistema archivia i dati; l’EDA è il modo in cui i sistemi comunicano. I due si combinano, ma nessuno dei due richiede l’altro. Un’altra riga che i fornitori sfumano: la consegna pub/sub dimentica – chi si sottoscrive in ritardo perde ciò che è scattato prima del suo arrivo – mentre lo streaming (il log ordinato in stile Kafka) ricorda, e lascia ai consumatori il replay da qualunque punto; la scelta dipende da quanto la storia faccia parte del prodotto.
Coreografia vs. orchestrazione
| Coreografia (nativa dell’EDA) | Orchestrazione | |
|---|---|---|
| Coordinamento | I servizi reagiscono agli eventi degli altri | Un coordinatore comanda ogni passo |
| Il workflow vive | In nessun punto esplicito – emerge | In un unico posto visibile e debuggabile |
| Accoppiamento | Minimo | L’orchestratore conosce tutti |
| Gestione dei guasti | Eventi compensativi, distribuiti | Retry e logica di errore centralizzati |
La regola specchiata, condivisa con la voce sull’orchestrazione: orchestrare quando qualcuno deve rispondere del risultato di un workflow; coreografare quando ai produttori davvero non importa cosa accade dopo. Le lunghe transazioni di business fra servizi prendono in entrambi i casi la forma di una saga – transazioni locali concatenate da eventi oppure da un orchestratore, annullate da compensazioni anziché da un rollback.
EDA quotidiana – senza broker
La sezione che i portali di architettura non scrivono mai: la maggior parte di chi sviluppa applicazioni pratica già pattern orientati agli eventi senza un broker di messaggi all’orizzonte. Un trigger beforeSave/afterSave è un consumatore di eventi di modifica dei dati. Un webhook è un evento che attraversa i confini fra aziende su semplice HTTP. Una live query è pub/sub con i client come subscriber. Un job in background accodato da un trigger è un evento che genera lavoro asincrono. Le schede di codice qui sopra sono esattamente questo stack – la scrittura è l’evento, il trigger è il router, e i consumatori sono un webhook, una coda e una sottoscrizione. I broker dedicati (Kafka, RabbitMQ, NATS – con CloudEvents come busta standard) si guadagnano il proprio peso operativo quando volume di eventi, replay e contratti fra team lo impongono; l’architettura comincia molto prima, e la domanda “serve un broker?” di solito si risponde da sé: non ancora.
Casi d’uso tipici
- Flussi di ordini e pagamenti – un fatto, molti reparti: la pipeline e-commerce canonica.
- Integrazione fra sistemi – servizi di team diversi che reagiscono senza chiamate dirette né deployment condivisi.
- Funzionalità in tempo reale – dashboard, feed e presenza come consumo di eventi lato client.
- Carichi a picchi – code che fanno da buffer alle raffiche, perché i consumatori le smaltiscano con i propri tempi.
- Audit e replay – log di streaming in cui “che cosa è accaduto, e in che ordine” è il prodotto stesso.
Conviene passare agli eventi? Matrice decisionale
| Situazione | Orientamento |
|---|---|
| Un fatto, più consumatori indipendenti | Eventi – il loro terreno d’elezione |
| Chi chiama ha bisogno della risposta per proseguire | Richiesta-risposta, senza imbarazzo |
| Workflow con un responsabile e uno SLA | Orchestrazione – comandare i passi |
| Reazioni dentro una sola app (e-mail all’iscrizione) | Trigger + job – EDA quotidiana |
| Notifiche fra aziende | Webhook |
| Stream ad alto volume, con replay, multi-team | Un broker vero – ora se l’è guadagnato |
Limiti e trade-off
- Il debug perde lo stack di chiamate. Una richiesta che si dirama in cinque salti asincroni è tracciabile solo con ID di correlazione propagati dal primo evento – aggiungerli a posteriori è penoso; conviene collegarli dal primo giorno.
- L’eventual consistency arriva fino all’interfaccia. L’utente modifica il profilo e la schermata successiva mostra il vecchio nome; o si progetta la UX per questo, o quel percorso resta sincrono.
- I duplicati sono contrattuali. La consegna at-least-once rende obbligatori i consumatori idempotenti – lo stesso contratto che impongono le code, perché è la stessa meccanica.
- Gli schemi evolvono sotto i piedi dei consumatori. Gli eventi sono contratti con subscriber sconosciuti: solo modifiche additive, topic versionati per le modifiche incompatibili e lettori tolleranti in ogni punto.
- I workflow emergenti si nascondono. La coreografia pura significa che nessuno può puntare il dito sul processo di business; quando serve a chi fa audit o a chi è in reperibilità, si aggiunge l’orchestratore.
I pattern orientati agli eventi su Back4app
Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. La sezione sull’EDA quotidiana è il suo elenco di funzionalità letto come architettura: ogni salvataggio è un evento, i trigger afterSave sono consumatori lato server con tutto l’ecosistema JavaScript, le Live Queries diffondono i fatti verso i client su WebSocket gestiti, i webhook li portano verso sistemi esterni e i job in background assorbono le reazioni lente – le schede di codice mostrano l’intero ciclo a partire da un solo salvataggio di ordine. Le discipline su cui questo articolo insiste restano in mano a chi sviluppa (after-hook idempotenti, ragionamento al passato, sincrono dove chi chiama ha bisogno di risposte), mentre l’infrastruttura in forma di broker – fan-out, consegna, flotte di connessioni – arriva come comportamento della piattaforma, e passare più tardi a un broker di streaming dedicato è un’aggiunta a questa architettura, non una sua riscrittura.
Domande frequenti
Che cos'è l'architettura orientata agli eventi in parole semplici?
I componenti annunciano che è accaduto qualcosa – un evento – invece di chiamarsi a vicenda, e ogni componente interessato reagisce con i propri tempi. Una trasmissione radio invece di una telefonata: il lavoro di chi annuncia finisce con l'annuncio, e gli ascoltatori vanno e vengono senza che chi annuncia cambi nulla.
Qual è la differenza tra un evento e un comando?
L'intenzione. Un comando è un imperativo rivolto a un solo destinatario, che può rifiutarlo – ShipOrder. Un evento è un fatto al passato diffuso a chiunque sia interessato – OrderShipped. L'insidia è il "comando passivo-aggressivo": un evento che in segreto pretende l'azione di un consumatore preciso, ovvero un comando travestito.
Qual è la differenza tra orientato agli eventi e richiesta-risposta?
L'accoppiamento nel tempo. Richiesta-risposta è sincrono – chi chiama attende, ha bisogno della risposta e fallisce se il destinatario è fuori servizio. Gli eventi sono fire-and-forget – resilienti e disaccoppiati, ma con eventual consistency (coerenza finale). La regola: se chi chiama ha bisogno del risultato per proseguire, richiesta-risposta; se il lavoro del produttore è concluso prima che inizino le reazioni, eventi.
Qual è la differenza tra EDA e pub/sub?
Il livello. Pub/sub è un pattern di messaggistica – un meccanismo di consegna, dai publisher ai topic e dai topic ai subscriber. L'EDA è lo stile architetturale che di solito si appoggia su quel meccanismo, insieme a code e stream. Il pub/sub è l'impianto idraulico; l'orientamento agli eventi è la filosofia di progettazione che quell'impianto serve.
Che cosa sono event notification, event-carried state transfer ed event sourcing?
I tre pattern distinti che Fowler riconosce sotto un unico nome. Notification: eventi sottili, i consumatori richiamano per i dettagli. State transfer: gli eventi portano i dati completi, quindi i consumatori ne conservano copie locali. Sourcing: una scelta di persistenza – registrare ogni cambiamento come evento e ricostruire lo stato con un replay. Il terzo descrive come un sistema archivia i dati, non come i sistemi comunicano.
Qual è la differenza tra coreografia e orchestrazione?
Coreografia: i servizi reagiscono agli eventi degli altri, senza cervello centrale – disaccoppiamento massimo, ma il workflow non esiste esplicitamente in nessun punto. Orchestrazione: un coordinatore comanda ogni passo – visibile e debuggabile, al prezzo di un punto di accoppiamento centrale. La regola: orchestrare quando qualcuno deve rispondere del risultato; coreografare quando ai produttori davvero non importa cosa accade dopo.
Come si gestiscono gli eventi duplicati e fuori ordine?
Per progetto, non per speranza: i broker consegnano at-least-once, quindi i consumatori devono essere idempotenti – deduplicare sull'ID dell'evento, oppure scrivere handler la cui ripetizione è innocua ("portare lo stato a spedito", mai "invertire"). L'ordine è garantito solo per partizione o per chiave; le architetture che pretendono un ordine globale combattono contro il proprio trasporto.
Quando conviene adottare un'architettura orientata agli eventi?
Quando uno stesso fatto ha più consumatori indipendenti, quando il carico è irregolare e un buffer aiuta, quando sistemi diversi devono integrarsi senza conoscersi, oppure quando una traccia di audit dei cambiamenti ha valore. Da evitare: il CRUD semplice, le letture, le operazioni che richiedono coerenza forte immediata e i team senza propensione per i sistemi distribuiti.