Event-Driven Design – Dalla definizione degli eventi all’implementazione con AxonIQ
Rieccoci con un nuovo articolo sul tema Event-Driven Design (EDD).
Nel nostro blog abbiamo infatti parlato del perché l’unione di CQRS ed Event Sourcing rappresenti una scelta architetturale molto efficace per sistemi complessi e scalabili.
Ma come può essere implementato questo paradigma? Come si progetta e si costruisce un’applicazione che utilizzi gli eventi come pilastri? Questo articolo guiderà attraverso il percorso di modellazione, partendo dalla definizione del linguaggio del dominio fino all’implementazione utilizzando Axon Framework.
Il punto di partenza: il linguaggio del dominio e gli eventi
Prima di iniziare a scrivere codice, è fondamentale individuare il “linguaggio” del dominio. Da buoni olandesi, gli inventori di Axon usano spesso come esempio la modellazione di un software per il noleggio di biciclette.
In questo esempio, il concetto di “Bicicletta prenotata” e “Inizio utilizzo della bicicletta” sono direttamente traducibili in eventi all’interno di un’architettura event-driven.
Di seguito riporto uno snippet della classe ReservedBikeEvent scritta in Java.
public class ReservedBikeEvent {
private final String bikeId;
private final String customerId;
private final LocalDateTime reservationDate;
}
Modellare il comportamento: Comandi e Aggregati
Gli eventi sono la conseguenza di azioni, che in Axon sono rappresentate dai comandi. Un Comando è una richiesta di eseguire un’azione che può modificare lo stato.
Nell’EDD l’interazione tra comandi ed eventi può essere modellata come nell’esempio in figura.

In Axon Framework è l’Aggregato che riceve e processa i comandi, costituendo un’unità fondamentale nel DDD.
L’aggregato è un oggetto di dominio che incapsula lo stato e le regole di business, e decide quali eventi generare in risposta a un comando.
Un esempio: Il flusso per prenotare una bicicletta
Di seguito, ecco come si presentano gli step per la prenotazione di una bicicletta, con Comando e Aggregato coinvolti:
- Un componente esterno invia un Comando (per esempio,
ReserveBikeCommand). - Il Comando viene indirizzato all’Aggregato corrispondente (esempio,
ReservationAggregate, ve lo mostrerò nel paragrafo successivo). - L’Aggregato valida il Comando secondo la sua logica di business, per esempio controllando che la bicicletta sia in stato disponibile.
- Se il Comando è valido, l’Aggregato genera un nuovo evento (esempio,
ReservedBikeEvent). - L’evento viene salvato in modo permanente nell’Event Store e pubblicato per notificare altri componenti del sistema.
Esempio di Aggregato: la classe ReservationAggregate
Di seguito riporto il codice della classe ReservationAggregate, in linguaggio Java:
public class ReservationAggregate {
@AggregateIdentifier
private String reservationId;
private BikeStatus status;
private String bikeId;
@CommandHandler
public ReservationAggregate(ReserveBikeCommand command) {
// 1. Validare il comando.
if (command.getBikeId() == null) {
throw new IllegalArgumentException();
}
// 2. Generare l'evento.
apply(new ReservedBikeEvent(
command.getBikeId(),
command.getCustomerId(),
LocalDateTime.now()
));
}
@EventSourcingHandler
public void on(ReservedBikeEvent event) {
// 3. Aggiornare lo stato interno dell'aggregato in base all'evento.
this.reservationId = UUID.randomUUID().toString();
this.bikeId = event.getBiciclettaId();
this.status = BikeStatus.RESERVED;
}
}
Query Handler: Separare la Lettura
Finora abbiamo modellato la parte di scrittura. Il pattern CQRS ci impone di separare la parte di lettura, e con Axon Framework possiamo gestirla con i Query Handler.
Quando un evento viene pubblicato, uno o più Query Handler possono ascoltarlo per aggiornare una vista ottimizzata dei dati, destinata esclusivamente alle query.
Esempio di Query Handler: la classe RentalStatusProjection
Continuiamo con l’esempio della prenotazione di una bicicletta con il codice che implementa il Query Handler RentalStatusProjection.
@Component
public class RentalStatusProjection {
private final BikeStatusViewRepository repository;
@EventHandler
public void on(ReservedBikeEvent event) {
BikeStatusView view = new BikeStatusView(
event.getBikeId(),
BikeStatus.RESERVED,
event.getCustomerId(),
event.getReservationDate()
);
repository.save(view);
}
@QueryHandler
public BikeStatusView handle(QueryBikeStatus query) {
return repository.findById(query.getBikeId()).orElse(null);
}
}
Questa separazione permette di scalare e ottimizzare scrittura e lettura in modo indipendente.
Orchestrazione di Processi Complessi: Saga
In sistemi reali e complessi, una singola operazione può coinvolgere più aggregati e generare una sequenza di eventi. Per coordinare molteplici eventi, Axon fornisce il pattern Saga (o Process Manager).
Una Saga è un componente che reagisce a eventi e può emettere nuovi Comandi a seconda della logica con cui è stato implementato.
Come funziona una Saga? Ecco un esempio.
- La Saga viene attivata da un evento iniziale (esempio,
ReservedBikeEvent). - In base allo stato interno, la Saga può inviare un comando a un altro aggregato (esempio,
UpdateAvailabilityCommand). - Attende l’evento di risposta (
UpdateAvailabilityEvent) per decidere il passo successivo. - Ripete finché il processo non è completato.
Vantaggi di Axon Framework
Axon Framework fornisce le basi per applicare questi concetti, riducendo enormemente gli sforzi dovuti alla reimplementazione dei fondamentali di questo paradigma. Tra i vantaggi offerti da questa soluzione di AxonIQ ricordo:
- Infrastruttura predefinita: gestisce automaticamente il Command Bus, l’Event Bus, il routing, la persistenza degli Aggregati e la gestione delle transazioni per gli eventi.
- Command Bus: Permette un’elaborazione asincrona e performante dei Comandi, parallelizzando dove possibile.
- Axon Server: Offre un Event Store su server, routing per i messaggi e AxonIQ Console per il monitoraggio, semplificando ulteriormente l’applicazione del paradigma.
Conclusioni
Modellare un’applicazione con AxonIQ significa adottare un cambio di prospettiva: dallo stato corrente alla cronologia degli eventi che lo ha generato. Il percorso parte dalla definizione del linguaggio degli eventi del dominio, e si concretizza nell’implementazione tramite comandi, eventi e aggregati, ma anche proiezioni e saghe per gestire letture e processi complessi.
Questa architettura, per quanto accompagnata da una modesta curva di apprendimento, ripaga in scalabilità, tracciabilità e resilienza.
Axon Framework mette a disposizione gli strumenti per implementare questo paradigma in progetti reali.