Daily Kanban meeting – Adattare le pratiche alle nostre esigenze

Per ogni nuovo progetto, in ideato si crea un team che va tipicamente dalle 2 alle 8 persone.

Aumentando la dimensione del team, di conseguenza aumenta la complessità nella sua gestione. Far comunicare  due persone (che spesso parlano lo stesso linguaggio tecnico) è ben diverso dal far comunicare otto persone che potrebbero essere sviluppatori web, sviluppatori mobile, designer, ux, ops, clienti con background non tecnico e dove alcune delle professionalità elencate potrebbero essere ricoperte da freelance inizialmente non del tutto allineati con la cultura aziendale e il metodo di lavoro.

In questo post vorrei raccontarvi la mia esperienza in un progetto, dove ci siamo accorti che lo stand-up meeting non era per nulla efficace e dove c’erano delle incomprensioni nascoste nell’utilizzo della kanban board.

(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.

CQRS ed Event Sourcing: Il nostro primo progetto andato in produzione – Parte 3 Testing

In ideato crediamo molto nella qualità del codice e di conseguenza nei test automatici. In Soisy non abbiamo fatto eccezione e come vedremo a breve, grazie alle componenti di Broadway, fare test unitari sulla nostra logica di business è veramente facile.

Anche in questo caso andremo ad implementare due tipologie di test unitari rispettivamente per la scrittura e per la lettura.

Come testare un comando (e quindi controllare che il giusto evento verrà salvato)

Negli articoli precedenti abbiamo visto come possiamo approvare un prestito ovvero:  “dato che un prestito è stato richiesto, quando viene inviato il comando ApproveLoan allora dovrà essere salvato un evento LoanApproved”.

Con broadway possiamo implementare il test proprio come descritto sopra:

Broadway implementa la classe Scenario che espone un’interfaccia fluente con in metodi given, when, then e internamente utilizza un InMemory repository come mock dell’event store. Cosi facendo i test sono isolati dal database e molto veloci.

Given prende come parametro un array di eventi che verranno applicati all’aggregato Loan il quale verrà quindi ricostruito e portato in un determinato stato, che per noi è quello che assume dopo che l’utente ha richiesto un prestito.

When prende un comando che verrà inviato all’aggregato Loan. In sostanza verrà chiamato il metodo Loan::approve il quale applicherà l’evento LoanApproved con lo stato desiderato ovvero con $status == ‘OK’ dato che stiamo testando un approvazione.

Then è il metodo che effettua l’asserzione di uguaglianza tra il vero evento applicato (actual) e l’evento LoanApproved che gli stiamo passando come parametro (expected).

Stiamo quindi testando che dato un comando verrà salvato l’evento che ci aspettiamo, con lo stato voluto.

Come testare la proiezione (e quindi che il giusto read model sia salvato)

Applichiamo gli stessi concetti per il testing del read model ovvero: “dato che è stato richiesto un prestito, quando il prestito verrà approvato allora il read model verrà correttamente aggiornato e salvato”.

Come potete vedere il gioco è sempre lo stesso solo che questa volta, la when prende un evento dato che il read model verrà modificato solo dopo che l’evento è accaduto. Il then si assicura che il read model generato dal codice di produzione sia quello che ci aspettiamo ovvero $expecetdLoan.

Broadway mette a disposizione nel repository git degli esempi di utilizzo.
Nel prossimo post vedremo come implementare un’api in cqrs + event sourcing.

CQRS ed Event Sourcing: Il nostro primo progetto andato in produzione – Parte 2 Read Side

Read Model

Avere un unico modello per lettura e scrittura, all’aumentare della complessità ci porta spesso in situazioni difficili da mantenere come ad esempio l’utilizzo di query complesse per reperire il modello stesso. Il read model ci permette di avere una rappresentazione “denormalizzata” del dato, che semplifica tantissimo la logica della view e delle query di lettetura. In soisy ad esempio una delle prime cose che dovevamo modellare erano i prestiti e quindi abbiamo creato un Aggregate\Loan.php  per modellare la scrittura e un ReadModel\Loan.php per modellare la lettura.

Questo oggetto rappresenta il dato da mostrare ed espone solo comportamenti utili alla presentazione.

I metodi serialize e deserialize come per l’evento servono rispettivamente preparare il dato ad essere salvato e per idratare l’oggetto per essere utilizzato.

Projector

I projector sono oggetti che stanno in ascolto su determinati eventi con i quali riescono a costruire i read model.

Ogni volta che un evento viene salvato nell’event store, tale evento viene “pubblicato” e inviato ai relativi subscriber (ovvero i projector), che utilizzeranno i dati trasportati dall’evento per costruire il read model relativo.

Vediamo un esempio:

Precedentemente abbiamo salvato l’evento LoanApproved. A questo punto broadway ci mette a disposizione un event bus che pubblica l’evento. L’event bus ha la responsabilità di consegnare gli eventi pubblicati a tutti le parti sottoscritte implementando il classico pattern publish/subscriber. Broadway la libreria che stiamo utilizzando per implementare CQRS/ES mette a disposizione questa interfaccia e due implementazioni funzionanti del bus.

In tutti i projector che stanno in ascolto su questo evento, verrà eseguito il metodo applyLoanApproved che prenderà il read model precedentemente salvato alla LoanWasRequested (1), lo aggiornerà con i nuovi dati arrivati (2) e lo salverà nel read model store (3).

Nel prossimo post parleremo di testing dei nostri comandi, eventi, projector e read model.