Axon 4.0: le novità
A due mesi dalla pubblicazione del primo articolo di questa serie, durante la stesura di questa seconda parte, axoniq [1] ha rilasciato la versione 4.0 di Axon Framework (al momento 4.0.2).
Questa versione ha come caratteristica principale una diversa organizzazione dei moduli del core e la creazione di estensioni, relative principalmente a connettori con DB e code varie, in maniera da poter far evolvere il framework e le estensioni in maniera differente.
Per quanto riguarda il core, sono stati aboliti tutti i costruttori in favore di implementazioni che utilizzano builder per la creazione degli oggetti. Inoltre è stato introdotto un Command Result Message che considera le risposte come messaggi. Rispetto alla versione precedente, sono stati eliminati tutti i metodi deprecati.
Cosa da non dimenticare, poi, è che con la nuova versione è stato cambiato il nome: da Axon Framework siamo passati a semplice Axon.
Axon Server
La nuova versione vede anche l’introduzione di un nuovo software a supporto del framework: adesso è disponibile in un solo prodotto ciò che prima erano due prodotti separati o che venivano sostituiti con prodotti diversi.
Sto parlando di Axon Server un prodotto che fonde i precedenti AxonDb e AxonHub: è rilasciato sotto licenza open source specifica AxonIQ, e rende disponibile un event store e un router event. In questo modo si facilita la vita degli sviluppatori, che avranno sicuramente una cosa in meno a cui pensare in fase di creazione del progetto; c’è infatti una effettiva semplificazione visto che si avrà a che fare con una configurazione sola, la quale, nel caso di integrazione con SpringBoot, risulta ancora più semplice.
È la vera novità di questa versione 4.0. Un server standalone che permette di salvare ed elaborare milioni — in verità miliardi — di eventi senza i problemi tipici di un server relazionale quando sia necessario scalare le prestazioni. Inoltre, come accennato prima, Axon Server si occupa di inoltrare i messaggi (eventi) alle varie applicazioni senza il bisogno di configurare nulla: è il server stesso che si occupa di trovare le applicazioni predisposte e auto-configurarsi nella maniera corretta.
Ovviamente l’uso di Axon Server non è “obbligatorio”: nulla ci vieta di non usarlo e di fare invece tutto con strumenti più “normali” come Mongo per la parte di event store o sistema di gestione delle code che supporta JMS. Ma, credetemi, ho provato Axon Server… e vale davvero la pena di usarlo.
La licenza AxonIQ
Come indicato precedentemente, il software è rilasciato sotto licenza open source specifica di AxonIQ, un po’ meno permissiva di quella Apache: non è possibile rilasciare derivati propri ma — cosa fondamentale — è permesso utilizzare il nostro software in qualunque ambiente. Esiste poi anche un pacchetto commerciale con codice chiuso.
Di seguito possiamo vedere quali componenti sono stati implementati da AxonIQ nella parte server e che cosa copre il framework. Possiamo che il framework va a integrarsi e non a sostituire il codice dell’applicazione.
Il processo di testing
La scorsa volta ho puntato a completare la nostra applicazione per la gestione delle GiftCard senza preoccuparmi troppo di implementare dei test per l’applicazione.
Questo perché molte parti usano caratteristiche native di SpringBoot [2], e quindi non necessitano di un vero e proprio test, che sarebbero solo dei mock inutili. Un esempio è rappresentato dalla parte di accesso al DB per la lettura dei dati: questa è fatta usando SpringData e le query sono generate dal framework, quindi testate per default dai test del framework.
CQRS
Ovviamente sarebbe cosa buona testare un pò tutta la parte legata all’implementazione del pattern CQRS. Un vantaggio che ci da il pattern è che possiamo esprimere i nostri test in termini di eventi e comandi, rendendo tutto molto semplice dal punto di vista funzionale.
Certamente la parte del Command Handling risulta molto complessa, considerando che, a fronte di un comando, deve generare un evento. In questo caso molto utile risulta la possibilità di definire degli scenari di test in termini di eventi e comandi e che rispecchiano lo schema seguente:
- dare degli eventi nel passato;
- eseguire un comando;
- aspettarsi determinati eventi da pubblicare o salvare.
Fortunatamente, per implementare i nostri test non serve avere un’infrastruttura attiva (p.e.: Axon Server o code e event store) ma basta utilizzare la parte del framework creata per i test.
Text Fixture
Il tutto si traduce inserendo una dipendenza con scope test all’interno del nostro progetto, il che permette di utilizzare un test fixture con cui implementare lo schema di comandi ed eventi definito prima.
AggregateTestFixture si propone proprio di configurare l’infrastruttura necessaria, in termini di command handler, repository, etc. ed esporre uno scenario in termini di “given – when – then”. In maniera sintetica la nostra fixture definisce 3 strategie:
- configurazione
- esecuzione
- validazione
Nella fase di configurazione si preparano i blocchi che servono all’esecuzione; vengono preparate versioni speciali per event store, command bus ed event bus.
Nella fase di esecuzione, definiamo i comandi da eseguire all’interno del command handling, mentre nell’ultima fase, quella di validazione, controlliamo che il risultato ottenuto sia quello aspettato.
Di seguito, possiamo vedere un piccolo esempio di come implementare un test:
I nostri test
Vediamo ora come implementiamo i nostri test. Come sempre, il codice si trova sul master del mio spazio su GitHub [3] da dove vi invito a scaricare il progetto, mandandomi poi eventuali suggerimenti o domande.
Versione di riferimento
Una delle prime cose da fare è stata quella di aggiornare la versione di Axon Framework alla ultima 3.x disponibile (3.4.1). Nonostante i suoi vantaggi, volutamente non sono per ora passato alla versione 4.0 perché questo comporta di sistemare svariate configurazioni e però, a livello di implementazione, non avrebbe dato un valore aggiuntivo, rispetto al codice già scritto.
Test Fixture
Fatta questa premessa, iniziamo a definire la nostra fixture che renderà disponibile l’infrastruttura necessaria per eseguire i nostri comandi e verificare le sequenze degli eventi, o eventuali eccezioni che dovessero presentarsi
Ricordiamoci che la nostra fixture funziona solo per un aggregato alla volta. Nel caso avessimo più aggregati, dobbiamo definire una fixture per oguno di essi; ma, secondo me, sarebbe meglio definire più classi di test.
I test veri e propri
Una volta creata la nostra fixture, cominciamo a definire i nostri test:
La struttura di ogni test risulta in linea con le tre fasi descritte precedentemente. Abbiamo la fase di configurazione (given) dove definiamo lo stato iniziale del nostro sistema. Nel caso della creazione di una nuova GiftCard non abbiamo lo stato precedente: utilizziamo quindi il metodo givenNoPriorActivity() per configurare uno stato vuoto. Eseguiamo il nostro comando (when) e verifichiamo (expectEvents) che l’evento sia quello atteso.
Come possiamo vedere, il tutto è molto semplice; il fatto poi che la nostra fixture sia costruita utilizzando il pattern builder rende anche elegante il nostro test.
Nel secondo test, possiamo vedere come controllare che i nostri check all’interno del metodo portino al lancio dell’eccezione nel caso provassimo a creare una GiftCard con valore 0.
Nell’ultimo test, infine, verifichiamo che, durante il processo di Redeem, il nostro stato cambi realmente, decrementando il valore finale della GiftCard.
Conclusioni
Dopo aver studiato Axon per utilizzarlo in un’applicazione reale in fase di sviluppo, posso dire che il pattern CQRS è molto potente e facile da integrare in quelle realtà dove una struttura legacy la fa da padrone. L’infrastruttura creata da AxonIQ è molto semplice da usare e con SpringBoot c’è solo da aggiungere qualche dipendenza nei nostri progetti.
Ovviamente il framework è solo un supporto: se sbagliamo a definire il modello rischiamo di complicare lo sviluppo e, a quel punto, districarci tra comandi ed eventi potrà diventare un incubo. Ma, con le dovute attenzioni, Axon è in grado di rendere la realizzazione dei nostri progetti più spedita e con meno errori: sicuramente il non dover pensare a molte altre cose semplifica la fase di configurazione della nostra applicazione.