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.
- È 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 sì. 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 (‘CQRS e Event Sourcing: il nostro primo progetto andato in produzione’ – Write side, Read side, Testing, Implementiamo un api) vi abbia chiarito un po’ le idee su questo pattern di cui si sente tanto parlare.
Noi 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.