Symfony Day: il nostro punto di vista

sf day logo small

La scorsa settimana siamo stati docenti, speaker e pubblico al Symfony Day, conferenza italiana itinerante dedicata al framework PHP, quest’anno a Milano presso l’auditorium Marinotti dell’Università degli studi Milano-Bicocca.

Giovedì, il giorno prima della conferenza, Riccardo, Michele e 14 volenterosi sviluppatori si sono “sporcati le mani” con CQRS/Event Sourcing. Una full immersion di 8 ore sull’argomento, dove oltre alla teoria s’è dato spazio a numerose esercitazioni pratiche.

La mattina successiva abbiamo raggiunto di buon’ora l’auditorium e ci siamo accaparrati i posti in prima fila, così da godere di un punto di vista privilegiato del palco, dove a turno sono saliti amici e speaker, tra cui Ettore e Simone.

(altro…)

CQRS ed Event Sourcing: Il nostro primo progetto andato in produzione – Parte 5 Conclusioni

Dopo un anno, ho notato sia aspetti positivi che negativi legati all’utilizzo di questo pattern. Alla domanda “lo rifaresti ?” ad oggi risponderei di si, ma solo per progetti che hanno domini complessi, ovvero con logiche complesse, flussi di business complessi e quindi domini che richiedono una buona flessibilità al cambiamento.

In Soisy ci siamo trovati a dover modellare flussi molto complessi, che abbiamo dovuto modificare molte volte per esigenze di business. Se siamo quindi in situazioni dove è necessario esplorare un dominio dato che a priori le informazioni che abbiamo non sono sufficienti o non sono corrette, Cqrs ed Event Sourcing ci permettono rendere il software flessibile e quindi di poter fare refactoring  in maniera relativamente semplice.

Ricordiamoci che non siamo obbligati a dover modellare tutto il nostro software in questa maniera, ma volendo solo le parti di dominio che riteniamo necessarie. In Soisy ad esempio abbiamo tutta la parte di firma digitale del contratto che non è stata realizzata in questo modo, dato che non ne sentivamo la necessità.

Andiamo adesso ad elencare un po’ di vantaggi e svantaggi.

Vantaggi:

  • è un pattern che pur se complesso permette di mantenere una complessità lineare all’interno del software. Dopo un anno di lavoro iterativo, basato sostanzialmente sull’implementazione e il refactoring continuo data la grande complessità del dominio, il software ha sempre risposto più che bene. Non ci siamo mai trovati in situazioni in cui “non potevamo fare delle cose”.
  • l’approccio ad eventi permette di modellare molto bene i flussi che avvengono all’interno del dominio. L’esempio visto rappresenta la prima implementazione dell’approvazione di un prestito quando ancora a livello di dominio non sapevamo bene di cosa stavamo parlando. Oggi l’approvazione è un flusso composto da diversi step tra cui l’interrogazione ai servizi CRIF ed altre fasi. Con un approccio ad eventi risulta relativamente facile poter mettersi prima o dopo un determinato evento per aggiungere nuova logica. Questa cosa ci ha permesso di poter cambiare strada molto velocemente durante le fasi di implementazione della funzionalità e quindi di esplorazione del dominio
  • come avete visto in fase di scrittura siamo sempre in modalità append only. Per ogni azione richiesta al sistema, ci saranno nuovi eventi salvati. Questo riduce la complessità in fase di scrittura in quanto concetti come “modifica” o “cancellazione” vanno sempre gestiti nella stessa maniera ovvero salvando eventi che rappresentano una modifica o una cancellazione
  • poter ricostruire ogni volta che vogliamo tutti i read model, ri-proiettando tutto l’event stream. L’event store è l’unica fonte di verità e proprio da questa fonte possiamo attingere per ricostruire read model specifici per le nostre esigenze. Pensate all’esempio in cui dovete implementare dei report statistici. Invece che impazzire costruendo query con molte join, nel nostro caso potreste implementare un nuovo projector che crea un nuovo read model adhoc per le nostre esigenze.
  • l’event store è a sua volta un log. Questo rende più semplice ricostruire cosa è accaduto nel sistema,  ad esempio per avere delle statistiche su un utente o per il debug. Abbiamo a disposizione tutto quello che è successo nel dominio del software e possiamo andare avanti e indietro nel tempo (requisito che in un dominio bancario è richiesto) in base alle nostre esigenze.
  • Il prossimo è un vantaggio legato a broadway. Con il componente per il testing che mette a disposizione, possiamo testare unitariamente e molto facilmente tutta la logica di dominio.
  • l’evento diventa il contratto da rispettare tra scrittura e lettura. Questo potrebbe permettere a persone differenti di poter implementare esclusivamente la parte di lettura o quella di scrittura. Essendo un pattern complesso, ogni volta che abbiamo dovuto introdurre un nuovo sviluppatore dentro Soisy, spesso l’abbiamo inizialmente messo a lavorare sulla parte di lettura che è sensibilmente più semplice.
  • può essere applicato solo per parti complesse. Nessuno ci obbliga ad applicare il pattern in tutte le parti del software.

 

Svantaggi:

  • Curva apprendimento lunga. Non è un pattern semplice e spesso viene applicato per domini complessi. Questo binomio fa si che nuovi sviluppatori che non conoscono il pattern, anche se esperti, non saranno da subito produttivi. Oltre a quello che avete visto in questa serie di post, in Soisy abbiamo anche implementato altri concetti come i processi e le saga, utili per flussi (anche asincroni) dove più aggregati devo parlare tra di loro.
  • E’ un pattern non molto comune ed ancora non molto utilizzato dalla comunità PHP. Durante i primi mesi di sviluppo ci siamo trovati in situazioni dove l’unica documentazione utile a risolvere alcuni dubbi è stata trovata in altre piattaforme, come .net e java.  Con un po’ più di confronto magari avremmo commesso meno errori nelle prime fasi di implementazione.
  • Diventa tremendamente svantaggioso per domini semplici come ad esempio applicazioni principalmente basati su CRUD o flussi semplici.
  • E’ un pattern abbastanza verboso. Personalmente non mi ha dato particolarmente fastidio, dato che con qualche “live templates” dell’IDE ho velocizzato operazioni ripetitive come la scrittura di comandi, eventi, handler e projector. Ho sentito delle obiezioni del tipo: “per fare questa roba devi creare tanti oggetti”. Vero!! Tanti piccoli oggetti molto facili da mantenere e che aumentano la leggibilità del codice. Del resto nella programmazione ad oggetti … ci sono gli oggetti.
  • Per certe applicazioni si potrebbe arrivare ad avere aggregati che con moltissimi eventi che potrebbero deteriorare le prestazioni (in fase di scrittura). I ragazzi di broadway rispondono: “se il tuo aggregato ha tanti eventi, probabilmente hai un problema di design”. Ci sono molti domini applicativi e casi differenti nello sviluppo software, quindi accetto e condivido questa risposta come provocazione, ma mi sembra anche che dia per scontate un po’ troppe cose. In Soisy siamo in produzione da diversi mesi e non abbiamo ancora avuto problemi del genere ma in un altro progetto che abbiamo in ideato si. La soluzione è stata quella di implementare gli snapshot ovvero delle fotografie dell’event store in determinati istanti di tempo. Lo snapshot diventa il nuovo punto di partenza da cui l’aggregato si ricostruirà molto più velocemente, ma aggiunge complessità al software.
  • Siccome PHP è un linguaggio sincrono, per implementare flussi asincroni necessita di un sistema di code e/o librerie aggiuntive, aggiungendo, di fatto, ulteriore complessità al nostro sistema.

 

Spero che questa serie di post vi abbia chiarito un po’ le idee su questo pattern di cui si sente tanto parlare.
In ideato continueremo ad utilizzarlo e a condividere le nostre esperienze sia positive che negative. In futuro cercheremo di pubblicare altri post che trattino altri aspetti che abbiamo dovuto affrontare come: eventi di compensazione dovuti a bug in produzione, deploy e  implementazione di saga per la realizzazione di flussi asincroni che hanno bisogno di salvare uno stato intermedio.

CQRS ed Event Sourcing: Il nostro primo progetto andato in produzione – Parte 4 Implementiamo un api per l’approvazione di un prestito

Per sviluppare Soisy abbiamo utilizzato symfony2broadway: prendendo come esempio uno dei casi d’uso dell’applicazione (opportunamente semplificato), in questo post vi mostreremo come l’abbiamo implementato.

Questo lo scenario: il cliente che ha richiesto un prestito dovrà fornire diverse informazioni (codice fiscale, nome, cognome ecc) . Alla fine di questo flusso verrà generata la richiesta di approvazione del prestito. L’approvazione avviene in tempo reale senza tempi di attesa.

Vediamo cosa succede lato backend quando viene invocata l’api per richiedere l’approvazione:

Command bus e repository per il read model Loan sono stati registrati come servizi nel container. In questo ci viene in aiuto il BroadwayBundle che registra dei servizi e mette a disposizione comandi da console per creare o cancellare l’event store.

Il comando viaggia sul command bus e viene gestito dal relativo Command Handler. Broadway utilizza la convezione handleNOME_COMANDO

Dopo che l’aggregato è stato ricostruito, viene chiamato il metodo approve.

Alla fine di ogni metodo di un aggregato viene chiamato il metodo apply che permette di collezionare una serie di eventi, che successivamente verranno salvati nell’event store grazie al repository utilizzato nel Command Handler.

In un approccio CQRS puro, avremo salvato l’aggragato vero e proprio. In event sourcing salviamo gli eventi, che rappresentano lo stato del nostro aggregato.

Come detto negli articoli precenti, una volta salvati gli eventi vengono pubblicati dall’event bus e mandati a tutti i projector che sono in ascolto. Broadway utilizza la convenzione handleNOME_EVENTO.

Questo metodo del projector aggiorna il read model e lo salva.

Il controllo torna al controller symfony che esegue una query ottenendo il read model aggiornato e lo restituisce al chiamante.

E’ importante notare che tutto il flusso avviene all’interno di una singola richiesta PHP, quindi in maniera sincrona. Nel caso in cui ci sia l’esigenza di renderlo asincrono c’è un interessante approccio che prevede l’utilizzo di code per la proiezione dei dati e quindi la costruzione dei read model.

Nel prossimo (e ultimo post) trarremo le conclusioni su questa esperienza che tuttora stiamo portando avanti anche su un altro progetto.