Documentazione dell'architettura event-driven con il modello C4 - Archyl Blog

Le architetture event-driven sono potenti ma notoriamente difficili da documentare. Gli eventi sono invisibili, asincroni e disaccoppiati per design. Questa guida mostra come usare il modello C4 per documentare i flussi di eventi, gli event channel e i pattern asincroni -- e come Archyl rende visibili i sistemi event-driven.

Documentazione dell'architettura event-driven con il modello C4

L'architettura event-driven e' diventata lo standard per costruire sistemi scalabili e loosely coupled. I servizi pubblicano eventi quando qualcosa accade. Altri servizi si sottoscrivono a quegli eventi e reagiscono. Il publisher non sa chi sta ascoltando. Il subscriber non sa chi ha pubblicato. Il sistema e' disaccoppiato, resiliente e flessibile.

Ed e' anche quasi invisibile.

Quando si guarda un diagramma C4 tradizionale di un sistema event-driven, si vedono servizi con frecce che puntano a un message broker. Il Servizio A pubblica su Kafka. Il Servizio B consuma da Kafka. Ma quali eventi fluiscono tra loro? Qual e' lo schema? Chi altro sta ascoltando? Cosa succede se un consumer fallisce? Il diagramma mostra l'infrastruttura ma nasconde il comportamento.

Documentare l'architettura event-driven richiede tecniche che rendono visibile l'invisibile -- che mostrano non solo l'infrastruttura (broker, code, topic) ma gli eventi stessi, i loro flussi, i loro schema e le loro garanzie. Questa guida copre come farlo usando il modello C4 e come le funzionalita' event channel di Archyl rendono i sistemi event-driven cittadini di prima classe nella documentazione dell'architettura.

Perche' i sistemi event-driven sono difficili da documentare

L'architettura event-driven introduce diverse sfide di documentazione che non esistono nei sistemi sincroni request-response.

Flusso di controllo invisibile

In un sistema sincrono, si puo' tracciare una richiesta dal client al server seguendo la catena di chiamate. Il Servizio A chiama il Servizio B, che chiama il Servizio C. Il flusso di controllo e' esplicito e visibile nel codice.

In un sistema event-driven, il flusso di controllo e' implicito. Il Servizio A pubblica un evento OrderCreated. Da qualche parte, il Servizio B reagisce a quell'evento riservando l'inventario. Da qualche altra parte, il Servizio C reagisce inviando un'email di conferma. Il Servizio A non sa nulla di B o C. Il flusso di controllo e' definito dalle sottoscrizioni agli eventi, non dal codice del publisher.

Questa indirezione significa che non si puo' capire il comportamento del sistema leggendo il codice di un singolo servizio. Serve una vista di livello superiore che mostri i flussi di eventi tra servizi -- e quella vista e' esattamente cio' che la documentazione dell'architettura dovrebbe fornire.

Relazioni many-to-many

Nelle architetture sincrone, le relazioni sono tipicamente one-to-one o one-to-few. Il Servizio A chiama il Servizio B. La relazione e' diretta e documentata dalla chiamata API.

Nelle architetture event-driven, le relazioni sono many-to-many. Un singolo tipo di evento potrebbe avere un produttore e cinque consumatori. Un singolo servizio potrebbe consumare eventi da dieci produttori diversi. Il grafo delle relazioni e' piu' denso e complesso che nei sistemi sincroni.

I diagrammi dell'architettura tradizionali faticano con questa densita'. Disegnare una freccia da ogni produttore a ogni consumatore attraverso ogni topic crea un diagramma che sembra un piatto di spaghetti. Serve un approccio di documentazione che mostri i flussi di eventi al giusto livello di astrazione.

Evoluzione degli schema

Gli schema degli eventi evolvono nel tempo. L'evento OrderCreated potrebbe iniziare con cinque campi e crescere a quindici in due anni. I consumatori potrebbero dipendere da campi specifici. Le modifiche allo schema possono rompere i consumatori se non sono retrocompatibili.

Documentare lo schema attuale e' necessario ma non sufficiente. Bisogna anche documentare la strategia di versionamento degli schema, le garanzie di compatibilita' e la cronologia delle modifiche non retrocompatibili.

Consistenza eventuale

I sistemi event-driven sono eventualmente consistenti per natura. Quando il Servizio A pubblica un evento, il Servizio B potrebbe elaborarlo millisecondi dopo o minuti dopo (se il consumatore e' in ritardo o se servono retry). Il sistema e' in uno stato inconsistente durante quella finestra.

La documentazione dovrebbe catturare questi confini di consistenza. Quali parti del sistema sono fortemente consistenti? Quali sono eventualmente consistenti? Qual e' il ritardo di propagazione atteso? Cosa succede durante la finestra di inconsistenza?

Dead letter queue e gestione degli errori

Quando un consumatore di eventi non riesce a elaborare un messaggio, l'evento tipicamente finisce in una dead letter queue (DLQ). Ma cosa succede poi? Chi monitora la DLQ? Qual e' la strategia di retry? Come vengono gestiti i messaggi avvelenati?

Questi pattern di gestione degli errori sono critici per il comportamento del sistema ma raramente vengono documentati. Fanno parte dell'architettura e dovrebbero essere visibili nella documentazione.

Modellare i sistemi event-driven con C4

Il modello C4 puo' rappresentare efficacemente le architetture event-driven con alcuni adattamenti a come si usa ogni livello.

Contesto di sistema: concentrarsi sui flussi dati, non sulle chiamate

Al livello del Contesto di Sistema, le architetture event-driven e sincrone appaiono simili. Il sistema interagisce con utenti e sistemi esterni. La differenza chiave e' nel modo in cui si etichettano le relazioni.

Invece di "chiama" o "interroga", usare etichette che descrivono il flusso di dati:

  • "Invia eventi ordine a"
  • "Riceve conferme di pagamento da"
  • "Pubblica eventi di analytics a"

Queste etichette suggeriscono la natura asincrona della comunicazione senza appesantire la vista ad alto livello con dettagli di implementazione.

Diagramma dei Container: rendere visibile il broker

Il diagramma dei Container e' dove l'architettura event-driven diventa distinta. Il message broker (Kafka, RabbitMQ, Amazon SQS/SNS, Google Pub/Sub) dovrebbe essere un container di prima classe nel diagramma, non un dettaglio di implementazione invisibile.

Ecco come potrebbe apparire un diagramma dei Container per un sistema e-commerce event-driven:

systems:
  - name: E-Commerce Platform
    type: software_system
    containers:
      - name: Order Service
        type: service
        technologies: [Go, PostgreSQL]
      - name: Inventory Service
        type: service
        technologies: [Java, PostgreSQL]
      - name: Notification Service
        type: service
        technologies: [Python, Redis]
      - name: Analytics Service
        type: service
        technologies: [Python, ClickHouse]
      - name: Event Bus
        type: queue
        technologies: [Apache Kafka]

relationships:
  - from: Order Service
    to: Event Bus
    label: "Publishes OrderCreated, OrderCancelled"
  - from: Event Bus
    to: Inventory Service
    label: "Delivers order events"
  - from: Event Bus
    to: Notification Service
    label: "Delivers order and payment events"
  - from: Event Bus
    to: Analytics Service
    label: "Delivers all domain events"
  - from: Inventory Service
    to: Event Bus
    label: "Publishes InventoryReserved, InventoryReleased"

Si noti come l'Event Bus e' al centro del diagramma, e le relazioni nominano esplicitamente i tipi di evento che fluiscono attraverso di esso. Questo rende i flussi di eventi visibili senza creare frecce dirette tra ogni produttore e ogni consumatore.

Event Channel: un concetto di prima classe

I singoli topic, le code e gli stream all'interno del message broker meritano la propria documentazione. Ogni event channel ha proprieta' che contano per comprendere il sistema:

  • Nome del canale: il topic Kafka, la coda RabbitMQ o la coda SQS
  • Tipi di evento: quali eventi fluiscono attraverso questo canale
  • Produttori: quali servizi pubblicano su questo canale
  • Consumatori: quali servizi consumano da questo canale
  • Serializzazione: come gli eventi vengono codificati (JSON, Avro, Protobuf)
  • Strategia di partizionamento: come gli eventi vengono distribuiti tra le partizioni
  • Policy di retention: per quanto tempo gli eventi vengono conservati
  • Garanzie di ordinamento: se l'ordine viene preservato e a quale granularita'

In Archyl, gli event channel sono un tipo di entita' dedicato. Si crea un event channel, se ne specificano le proprieta' e lo si collega ai servizi che producono e consumano da esso. Questo crea un modello strutturato e interrogabile dei flussi di eventi.

Ad esempio, un event channel "orders" potrebbe essere documentato cosi':

  • Nome: orders
  • Broker: Kafka
  • Produttori: Order Service
  • Consumatori: Inventory Service, Notification Service, Analytics Service, Billing Service
  • Tipi di evento: OrderCreated, OrderUpdated, OrderCancelled, OrderCompleted
  • Serializzazione: Avro con Schema Registry
  • Partizionamento: per order ID
  • Retention: 7 giorni

Questo livello di dettaglio rende visibile e interrogabile l'infrastruttura invisibile. Quando uno sviluppatore ha bisogno di sapere chi sta consumando gli eventi degli ordini, la risposta e' documentata e trovabile.

Diagramma dei Componenti: Event Handler e Publisher

Al livello dei Componenti, i servizi event-driven hanno strutture interne distintive che vale la pena documentare per i servizi complessi:

  • Event Handler: componenti che consumano ed elaborano tipi di evento specifici
  • Event Publisher: componenti che producono eventi
  • Saga / Process Manager: componenti che orchestrano workflow multi-step attraverso gli eventi
  • Proiezioni: componenti che costruiscono modelli di lettura dagli stream di eventi

Un diagramma dei Componenti per l'Order Service potrebbe includere:

  • Order Controller -- gestisce le richieste HTTP per la gestione ordini
  • Order Processor -- logica di business core per la creazione e validazione degli ordini
  • Event Publisher -- pubblica OrderCreated, OrderUpdated, OrderCancelled su Kafka
  • Payment Event Handler -- consuma gli eventi PaymentProcessed e PaymentFailed
  • Order Saga -- gestisce il workflow di evasione dell'ordine tra servizi

Documentare questi componenti quando il servizio e' abbastanza complesso da giustificarlo -- particolarmente per i servizi che partecipano a pattern di coreografia o orchestrazione.

Documentare i pattern event-driven comuni

Certi pattern appaiono ripetutamente nei sistemi event-driven. Documentarli esplicitamente risparmia ai team di doverli ricostruire al contrario dal codice.

Event Sourcing

Nei sistemi con event sourcing, lo stato di un'entita' viene derivato da una sequenza di eventi piuttosto che memorizzato come snapshot. Lo stream di eventi e' la fonte di verita' e lo stato attuale e' una proiezione.

Documentare l'event sourcing:

  • Identificare quali entita' usano l'event sourcing
  • Elencare i tipi di evento nello stream di eventi di ogni entita'
  • Documentare le proiezioni che derivano modelli di lettura dagli stream
  • Annotare la strategia di snapshotting (se presente)

CQRS (Command Query Responsibility Segregation)

CQRS separa le operazioni di scrittura (comandi) dalle operazioni di lettura (query), spesso usando eventi per mantenere sincronizzato il modello di lettura con il modello di scrittura.

Documentare CQRS:

  • Separare chiaramente i container lato comando dai container lato query nel modello C4
  • Documentare il flusso di eventi dal lato scrittura al lato lettura
  • Annotare il modello di consistenza (quanto in ritardo puo' essere il modello di lettura)

Coreografia vs. Orchestrazione

Nella coreografia, i servizi reagiscono agli eventi indipendentemente. Non esiste un coordinatore centrale. Nell'orchestrazione, un servizio centrale (l'orchestratore o saga) coordina il workflow inviando comandi e ascoltando le risposte.

Documentare quale pattern il sistema usa per ogni workflow. Se si usa la coreografia, documentare la sequenza attesa di eventi e i servizi che partecipano. Se si usa l'orchestrazione, documentare la macchina a stati della saga e i comandi che emette.

Dead Letter Queue e pattern di retry

Documentare la strategia di gestione degli errori per l'elaborazione degli eventi:

  • Quali eventi hanno dead letter queue?
  • Qual e' la policy di retry (conteggio, strategia di backoff)?
  • Chi e' responsabile del monitoring e della rielaborazione degli eventi DLQ?
  • Quale alerting e' in atto per l'accumulo nella DLQ?

In Archyl, si possono modellare le DLQ come event channel aggiuntivi collegati ai canali primari. Questo rende l'infrastruttura di gestione degli errori visibile nella documentazione dell'architettura.

Documentare gli schema degli eventi

Gli schema degli eventi sono contratti tra produttori e consumatori. Meritano lo stesso livello di documentazione dei contratti API nei sistemi sincroni.

Documentazione degli schema

Per ogni tipo di evento, documentare:

  • Nome dell'evento: un nome chiaro e specifico del dominio (OrderCreated, non GenericEvent)
  • Versione dello schema: la versione attuale dello schema
  • Campi: tutti i campi con i loro tipi, descrizioni e se sono obbligatori o opzionali
  • Payload di esempio: un esempio rappresentativo JSON/Avro/Protobuf
  • Compatibilita': se lo schema e' compatibile in avanti, all'indietro o completamente

La funzionalita' API Contract di Archyl puo' essere usata per documentare gli schema degli eventi insieme alle specifiche REST e gRPC. Collegare il contratto all'event channel per creare una connessione diretta tra lo schema e l'infrastruttura.

Strategia di evoluzione degli schema

Documentare l'approccio del team all'evoluzione degli schema:

  • Si usa uno schema registry (Confluent Schema Registry, AWS Glue)?
  • Quale modalita' di compatibilita' e' applicata (backward, forward, full)?
  • Come vengono comunicate le modifiche non retrocompatibili ai consumatori?
  • Qual e' il processo di deprecation per le vecchie versioni degli schema?

Queste informazioni appartengono a un ADR collegato all'infrastruttura degli eventi. E' una decisione che impatta l'intero sistema e dovrebbe essere documentata una volta, chiaramente, e referenziata da tutti i team.

Visualizzare i flussi di eventi

I diagrammi statici possono mostrare l'infrastruttura degli eventi, ma faticano a mostrare i flussi di eventi -- la sequenza di eventi che implementa un processo di business.

Usare i Flow per i processi di business

La funzionalita' Flow di Archyl permette di documentare la sequenza di eventi che implementa un processo di business. Ad esempio, un flusso "Inserimento ordine" potrebbe mostrare:

  1. Il cliente invia l'ordine tramite la Web App
  2. L'API Gateway inoltra la richiesta all'Order Service
  3. L'Order Service valida e persiste l'ordine
  4. L'Order Service pubblica l'evento OrderCreated su Kafka
  5. L'Inventory Service consuma OrderCreated, riserva l'inventario
  6. L'Inventory Service pubblica l'evento InventoryReserved
  7. Il Payment Service consuma InventoryReserved, elabora il pagamento
  8. Il Payment Service pubblica l'evento PaymentProcessed
  9. Il Notification Service consuma PaymentProcessed, invia l'email di conferma

Questo flusso mostra il comportamento end-to-end che emerge dall'architettura event-driven. Il codice di nessun singolo servizio rivela questo flusso -- esiste solo a livello di architettura.

Usare gli overlay per viste diverse

Creare overlay per mostrare diversi aspetti dell'architettura event-driven:

  • Overlay dei flussi di eventi: evidenzia solo le relazioni relative agli eventi, nascondendo la comunicazione sincrona
  • Overlay produttore/consumatore: codifica a colori i servizi in base a se producono, consumano o entrambi
  • Overlay della gestione errori: mostra DLQ, percorsi di retry e monitoring

Gli overlay permettono di creare viste focalizzate da un singolo modello architetturale, evitando la necessita' di diagrammi multipli ridondanti.

Best practice

Denominare gli eventi con azioni di dominio, non operazioni tecniche

Usare nomi come OrderCreated, PaymentFailed, InventoryReserved -- non DataUpdated, MessageSent o RecordInserted. I nomi specifici del dominio rendono i flussi di eventi leggibili a livello di architettura.

Documentare la decisione "perche' non sincrono"

Per ogni interazione event-driven, c'e' stata una decisione di usare la comunicazione asincrona invece di quella sincrona. Documentare quella decisione. Perche' l'Order Service pubblica un evento invece di chiamare direttamente l'Inventory Service? La risposta (disaccoppiamento, resilienza, scalabilita') dovrebbe essere catturata in un ADR.

Mantenere la documentazione degli event channel vicina al codice

Se gli schema degli eventi sono definiti nel codice (file Protobuf, schema Avro, JSON Schema), collegare la documentazione dell'architettura a quei file. Questo crea una connessione tra la documentazione astratta e l'implementazione concreta.

Revisionare i flussi di eventi durante le revisioni dell'architettura

Durante le revisioni trimestrali dell'architettura, percorrere i flussi di eventi documentati. Chiedere:

  • Ci sono nuovi tipi di evento che non sono documentati?
  • Ci sono tipi di evento documentati che non sono piu' usati?
  • Sono stati aggiunti nuovi consumatori senza aggiornare la documentazione?
  • Gli schema documentati sono ancora accurati?

Il rilevamento della deriva di Archyl aiuta a rispondere automaticamente a queste domande, ma la revisione umana periodica cattura cose che i controlli automatizzati non rilevano.

Conclusione

La documentazione dell'architettura event-driven richiede uno sforzo intenzionale per rendere visibile l'invisibile. Gli eventi, per design, disaccoppiano i produttori dai consumatori. Questo e' un punto di forza architetturale ma una sfida per la documentazione.

Il modello C4 fornisce il framework: Contesto di Sistema per il quadro generale, diagrammi dei Container con il message broker come elemento di prima classe, event channel per la documentazione dettagliata di topic e code, e diagrammi dei Componenti per event handler e saga complessi.

Archyl fornisce gli strumenti: event channel come entita' di prima classe, flow per documentare i processi di business event-driven, contratti API per gli schema degli eventi, overlay per viste focalizzate e rilevamento della deriva per catturare le modifiche non documentate.

Documentare l'architettura event-driven nello stesso modo in cui la si progetta: con intenzione, struttura e la consapevolezza che il comportamento del sistema emerge dalle interazioni tra servizi, non da un singolo servizio.

Inizia con Archyl e rendi la tua architettura event-driven visibile, documentata e compresa.