← Blog Software architecture

Event-driven architectures: when they really pay off (and when they don't)

TL;DR

[EN draft] Event-driven conviene quando ci sono molti consumatori dello stesso evento, processi long-running, picchi di carico irregolari, requisiti di resilienza forti. Non conviene per applicazioni piccole, processi sincroni di breve durata, team senza esperienza pregressa di message broker.

English version draft — full translation in F3. Italian content preserved for technical fidelity.

L'architettura event-driven (EDA) e' uno dei pattern piu' citati dei convegni tecnici degli ultimi cinque anni. Spesso pero' viene proposta come default senza valutarne il costo reale: complessita' operativa, debugging piu' difficile, ordering e idempotenza da gestire, monitoring distribuito necessario.

In questo articolo distilliamo i criteri che applichiamo per decidere se un sistema deve essere event-driven, parzialmente event-driven (hybrid) o restare sincrono.

Cosa intendiamo per event-driven

Una architettura event-driven e' un sistema in cui i componenti comunicano emettendo e consumando eventi tramite un broker (Kafka, RabbitMQ, AWS SNS+SQS, Google Pub/Sub, NATS). Il produttore non conosce il consumatore. Lo stesso evento puo' avere zero, uno o N consumatori.

Distinguiamo da: chiamate API sincrone (REST/GraphQL), code di lavoro semplici (Celery, BullMQ — sono pattern producer-consumer 1-a-1, non veri eventi pubblici), pattern CQRS (separazione comando/query — ortogonale a EDA).

Quattro segnali per scegliere event-driven

Segnale 1 — Molti consumatori dello stesso fatto di business. Quando un evento di business ("ordine ricevuto", "utente registrato", "transazione completata") deve essere reagito da N sottosistemi (notifica email, aggiornamento warehouse, fatturazione, antifrode, analytics), pubblicare l'evento una volta e farlo consumare da ognuno e' molto piu' pulito di N chiamate API sincrone.

Segnale 2 — Processi long-running con stati intermedi visibili. Un workflow di onboarding cliente che richiede 5-7 giorni con verifiche manuali, integrazioni esterne lente, attese di feedback umano, beneficia di una macchina a stati event-driven con eventi persistenti come fonte di verita'.

Segnale 3 — Picchi di carico irregolari. Sistemi che ricevono raffiche di traffico (e-commerce in saldi, broadcast di notifica, batch fiscale fine mese) beneficiano del disaccoppiamento temporale fra producer e consumer: il broker assorbe il picco, i consumer elaborano al ritmo che riescono.

Segnale 4 — Requisiti di resilienza alti. Se la perdita di un evento e' inaccettabile (transazioni finanziarie, ordini, audit log) il broker con persistence e ack-on-success offre garanzie at-least-once che la chiamata HTTP sincrona non da'.

Tre situazioni in cui event-driven non conviene

Situazione 1 — Applicazione monolitica piccola. Un sistema con 5-10 endpoint, traffico modesto, team di 2-3 sviluppatori. EDA introduce piu' complessita' operativa di quanta ne risolva.

Situazione 2 — Processi sincroni con feedback immediato. Un checkout di e-commerce che deve restituire all'utente "pagamento OK / KO" in 2 secondi. La latenza del broker + il routing aggiuntivo penalizzano l'UX. Meglio sincrono con timeout.

Situazione 3 — Team senza esperienza pregressa. Adottare EDA richiede competenze di message broker tuning, gestione dei dead-letter queue, observability distribuita (tracing), idempotenza. Senza queste competenze, il team si trova ingaggiato in un debugging molto piu' complesso del previsto.

Il pattern ibrido che funziona bene

Vediamo funzionare bene un'architettura ibrida: API REST sincrona per le interazioni utente in tempo reale, eventi per i processi asincroni "a valle". Esempio: l'utente conferma un ordine — API sincrona valida, salva in DB, emette un evento OrderCreated e risponde 201 in 200ms. Dietro le quinte, 5 consumer riceveranno l'evento: warehouse, fatturazione, email, analytics, antifrode.

Questo pattern combina UX reattiva con disaccoppiamento operativo, e non richiede di trasformare tutto il sistema in EDA puro.

Scelta del broker

Tre famiglie principali:

Kafka — eventi persistiti per tempo configurabile, throughput altissimo, complessita' operativa significativa. Indicato per: stream processing real-time, sistemi con milioni di eventi/giorno, audit log persistente.

RabbitMQ — message broker classico, routing flessibile, throughput minore di Kafka ma piu' semplice da gestire. Indicato per: code di lavoro affidabili, integrazioni B2B, casi medi.

Cloud-managed (SNS/SQS, Pub/Sub, EventBridge) — il broker gestito dal cloud provider, zero operations, costi per evento. Indicato per: tutti i progetti che hanno gia' scelto un cloud, salvo requisiti specifici di persistenza o multi-cloud.

Pattern e antipattern

Pattern utili:

  • Outbox pattern — scrivi l'evento nella stessa transazione DB del cambio di stato, poi un processo separato lo pubblica. Garantisce atomic write + at-least-once delivery.
  • Saga — workflow distribuito con compensation per i rollback. Utile per transazioni distribuite.
  • Event sourcing — lo stato del sistema deriva integralmente da una sequenza di eventi. Potente ma costoso, da scegliere consapevolmente.

Antipattern frequenti:

  • EDA solo perche' di moda — applicazione piccola, team piccolo, problemi inesistenti. Costo gratuito di complessita'.
  • Eventi anemic — eventi che contengono solo l'ID, costringendo i consumer a fare chiamate REST sincrone per arricchire. Vanifica il disaccoppiamento.
  • Niente schema registry — versioning degli eventi gestito ad-hoc. Dopo 6 mesi il sistema non sa piu' quali campi sono backward-compatible.

Costi che spesso vengono sottovalutati

Adottare EDA implica costi nascosti che vanno calcolati in fase di scelta.

Observability piu' costosa. Distributed tracing (OpenTelemetry, Jaeger) e' obbligatorio per fare debug di flussi cross-service. Setup iniziale 5-10 giorni-uomo, manutenzione continua.

Test piu' difficili. Test di integrazione richiedono broker reali o mock fedeli. Test end-to-end di flussi event-driven richiedono testbench che esegue tutti i consumer.

Operations 24/7. Il broker e' single point of failure. Va monitorato, dimensionato, aggiornato. Cluster Kafka in produzione richiede competenze specifiche e on-call.

FAQ

Si puo' partire monolitico e migrare a event-driven?

Si', e' anzi la strategia consigliata. Partire con un monolite ben modulato, identificare i bounded context, estrarre il primo servizio quando emerge un caso che lo giustifica. Migrazione graduale.

Quanti consumatori giustificano un broker rispetto a chiamate API?

Soglia empirica: oltre 3 consumatori dello stesso evento di business, oppure 1 solo consumatore che pero' deve sopravvivere a periodi di down del producer. Sotto, API sincrone vanno meglio.

Event-driven funziona con PHP/Laravel?

Si', tramite estensioni come Laravel Horizon + Redis, oppure Spatie/event-sourcing per pattern piu' avanzati. Per casi enterprise pero' tipicamente si introduce un broker dedicato (RabbitMQ, Kafka) con i worker scritti in PHP/Node a seconda della specializzazione.

Quanti eventi al giorno giustificano Kafka?

Sotto 100k eventi/giorno, qualsiasi broker (anche cloud-managed) va bene e Kafka e' sovradimensionato. Sopra 10M/giorno, Kafka diventa naturalmente la scelta. Nel mezzo, dipende da pattern (retention, replay).

Conclusione

EDA e' uno strumento potente quando applicato dove serve. Applicato male introduce complessita' senza beneficio. La decisione richiede analisi del problema, non adesione a trend. Per discutere il vostro caso, apriamo un confronto tecnico.

A software problem you don't know who to entrust?

A free 30-minute conversation to scope the project.

Open a conversation