Vai al contenuto
Pattern

Pattern EDA: coreografia, orchestrazione e tipi di eventi 💃🕺

L’EDA non è “un pattern”. È una famiglia di pattern. E come tutte le famiglie, può essere adorabile o drammatica, dipende da quanto la governi.

Coreografia vs orchestrazione (cosa stai davvero scegliendo) 🎭

Coreografia 🧩

    flowchart LR
  A[Order Service] -->|OrderCreated| EB[(Event broker)]
  EB --> P[Payment Service]
  EB --> I[Inventory Service]
  P -->|PaymentAuthorized| EB
  I -->|InventoryReserved| EB
  

Ogni servizio reagisce agli eventi che gli interessano e pubblica nuovi eventi.

  • Vantaggi: decentralizzata, scalabile, aggiungi consumer senza toccare il producer.
  • Svantaggi: il flusso di business può diventare difficile da “vedere” (e da debuggare) senza strumenti.

Quando funziona bene:

  • side effects indipendenti;
  • workflow “a cascata” semplice;
  • team maturi su osservabilità.

Orchestrazione 🧭

    flowchart LR
  O[Orchestratore / Workflow] -->|comanda step| S1[Servizio A]
  O -->|comanda step| S2[Servizio B]
  S1 -->|evento di esito| EB2[(Event broker)]
  S2 -->|evento di esito| EB2
  EB2 --> O
  

Un componente (workflow/orchestrator) dirige i passi e decide il prossimo step.

  • Vantaggi: visibilità del processo, controllo, gestione errori più centrale.
  • Svantaggi: rischi un nuovo monolite (solo più elegante), coupling al workflow.

Quando funziona bene:

  • processi multi-step con vincoli forti;
  • necessità di controllare timeout, retry, compensazioni in modo coerente.

Event Notification 📨

La variante più minimale di payload: l’evento annuncia che qualcosa è successo, ma porta pochissimi dati — di solito solo un ID o un riferimento al soggetto coinvolto. Il consumer che riceve la notifica, se ha bisogno di dettagli, fa una chiamata diretta al producer per recuperare lo stato aggiornato.

    sequenceDiagram
  Producer->>Broker: OrderCreated { orderId: 42 }
  Broker->>Consumer: OrderCreated { orderId: 42 }
  Consumer->>Producer: GET /orders/42
  Producer-->>Consumer: { stato completo }
  
  • Vantaggi: contratti piccoli e stabili, cambio di stato interno senza rinegoziare il contratto dell’evento, rischio minore di esporre dati sensibili nel canale asincrono.
  • Svantaggi: reintroduce coupling sincrono in forma camuffata — il consumer deve chiamare il producer dopo aver ricevuto l’evento. Se il producer è irraggiungibile in quel momento, il processing fallisce o richiede logica di retry separata. In scenari ad alto volume può generare un “thundering herd” di callback simultanee.

Quando funziona bene:

  • dati sensibili che non vuoi replicare nel broker (GDPR, informazioni riservate);
  • stato che cambia spesso ma con pochi consumer realmente interessati ai dettagli;
  • vuoi mantenere il contratto dell’evento minimo e disaccoppiato dall’implementazione interna.

La trattazione completa — criteri di scelta, confronto con ECST e trade-off su coupling sincrono — è nella guida su event design.

Event-Carried State Transfer (ECST) 📦

L’alternativa alla notifica: metti nello stesso evento tutto (o quasi) lo stato rilevante, così il consumer riceve già quello che gli serve per elaborare l’evento senza dover richiamare nessuno.

    sequenceDiagram
  Producer->>Broker: OrderCreated { orderId: 42, userId: 7, items: [...], totale: 89.90, ... }
  Broker->>Consumer: OrderCreated { orderId: 42, userId: 7, items: [...], totale: 89.90, ... }
  Note over Consumer: elabora senza callback
  
  • Vantaggi: il consumer è autonomo a runtime — può processare l’evento anche se il producer è temporaneamente irraggiungibile, zero roundtrip aggiuntivi, latenza più prevedibile. È il pattern naturale per costruire read model denormalizzati (tipico in CQRS) o per scenari ad alto throughput dove ogni roundtrip in più è costoso.
  • Svantaggi: payload più grandi (impatto su bandwidth, storage nel broker, costi di rete), rischio di esporre nello stream dati sensibili che andrebbero protetti, e — il punto più sottile — schema evolution diventa più complessa: ogni campo nel payload diventa parte del contratto pubblico, e rimuovere o rinominare qualcosa può rompere consumer che nemmeno ti aspetti.

C’è anche il problema dell’ordine: se arrivano eventi fuori sequenza e il consumer aggiorna il proprio stato locale, può trovarsi con dati stantii anche se tecnicamente “più recenti” secondo il timestamp locale.

Quando funziona bene:

  • consumer che devono essere altamente autonomi o operare in modalità offline-first;
  • scenari ad alto throughput dove il costo del callback è significativo;
  • costruzione di read model specializzati (projection/materialized view in stile CQRS).

Per la trattazione completa con criteri di scelta, confronto con Event Notification e strategie su schema evolution: vedi la guida su event design.

Event Sourcing e CQRS 🏛️

Nei sistemi EDA maturi, prima o poi arriva la domanda: “ci basta reagire agli eventi, o dobbiamo anche ricostruire stato e ottimizzare la lettura?”. Qui entrano in gioco Event Sourcing (la storia degli eventi come fonte di verità immutabile) e CQRS (modello di scrittura e modello di lettura separati).

Non sono obbligatori: servono quando audit, replay e read model specializzati sono requisiti reali, non desideri da whiteboard. Per una trattazione completa — snapshot, schema evolution e criteri di adozione — vedi la guida dedicata su Event Sourcing e CQRS.

Cosa decidere davvero (senza perdersi in teoria) 🧰

Questa guida non serve a collezionare pattern come figurine: serve a scegliere. Quando stai disegnando un flusso event-driven, le decisioni che contano davvero sono poche e abbastanza “brutali”.

  • Coreografia: se il flusso è lineare e i side effects sono indipendenti, di solito basta. Esempio: OrdineCreato → riserva magazzino → conferma; eviti un singolo punto centrale, ma devi accettare che coordinamento e timeout siano “distribuiti”.
  • Orchestrazione: se ti serve visibilità e controllo centralizzato del processo (stato, timeout, compensazioni coerenti), ha senso. Utile anche per politiche centralizzate (retry, circuit breaker) su processi multi-step, ma ti porti a casa più coupling e più manutenzione sui workflow.
  • Notification: se vuoi contratti più stabili e piccoli, riduce il payload. Ma spesso reintroduce coupling sincrono: i consumer devono fare fetch dal producer e l’asincronia diventa una dipendenza “a latenza variabile”.
  • ECST: se vuoi consumer molto autonomi, riduce callback/roundtrip. In cambio aumentano responsabilità e rischi su payload (dimensione/bandwidth), privacy e schema evolution/versioning.
  • Event Sourcing / CQRS: non sono decisioni da prendere al primo whiteboard. Diventano rilevanti quando hai requisiti concreti di audit, replay o read model molto specializzati. Se non sai ancora se ne hai bisogno, probabilmente non ne hai bisogno. Se e quando lo capisci, vedi la guida dedicata.

Nota pratica: qualunque scelta fai, preparati a misurarla. Senza osservabilità (correlation ID, tracing/logging decenti) “coreografia vs orchestrazione” diventa una discussione filosofica… e di solito vince il caos.

Prossimi passi 🚀

Ora che hai scelto come disegnare i flussi (coreografia/orchestrazione) e che stile di payload usare, il resto è disciplina: contratti e operatività.

Ultimo aggiornamento il