Il pattern CQRS (Command Query Responsibility Segregation) è un modello architetturale che separa il dominio delle operazioni di lettura (query) dal dominio delle operazioni di scrittura (command) all’interno di un’applicazione. Questo modello favorisce il Single Responsibility Principle il quale afferma che ogni elemento di un programma (es. classe o metodo) deve avere una sola responsabilità, e che tale responsabilità debba essere interamente incapsulata nell’elemento stesso. Il modello CQRS è molto utilizzato in architetture a microservizi in quanto consente di aumentarne la granularità grazie alla separazione dei due flussi command e query.
CRQR vs CRUD
Per comprendere il modello è utile confrontarlo con il tradizionale modello CRUD (Create, Read, Update, Delete) largamente utilizzato. Tale modello prevede lo stesso modello dati sia per l’esecuzione delle query che per gli aggiornamenti, rendendolo semplice da implementare, anche con il supporto dei framework ORM, che riescono ad automatizzare agevolmente alcuni processi. Consideriamo, ad esempio, un’applicazione di e-commerce in cui gli utenti possono visualizzare prodotti, aggiungerli al carrello e effettuare un ordine. Con l’approccio CRUD, avremmo una singola entità “Prodotto” ed operazioni di base per creare un nuovo prodotto, leggere i dettagli di un prodotto, aggiornare le informazioni di un prodotto esistente e eliminarlo dal sistema. Queste operazioni CRUD agiscono direttamente sulla tabella del database “Prodotto” e ne manipolano lo stato persistente.
Se, ad esempio, vogliamo eseguire una operazione di aggiornamento della disponibilità di un prodotto in magazzino, dovremmo agire direttamente sulla relativa istanza della classe “Prodotto”, aggiornando la proprietà “quantità” e salvare sulla base dati. A livello di servizio possiamo quindi identificare due metodi di base che sono: leggiProdotto e salvaProdotto.
La semplicità del modello CRUD è controbilanciata da una maggiore difficoltà di gestione e manutenzione in caso di applicazioni sempre più complesse, che possono portare ad una elevata proliferazione delle classi. Capita molto spesso, infatti, che nel recupero dei dati sia necessario aggregare le informazioni in entità più complesse rispetto a quelle base, e conseguentemente è necessario utilizzare modelli dati diversi rispetto a quelli base con cui i dati sono memorizzati.
Il pattern CQRS, diversamente da quanto accade per quello CRUD, prevede modelli dati dedicati per le operazioni di lettura e scrittura, come previsto dal principio CQS (Command Query Separation). Tornando all’esempio dell’applicazione e-commerce, esisteranno due classi distinte: una per i servizi di tipo command ed una per quelli di tipo query. Inoltre, a livello command esisterà un metodo per l’aggiornamento della disponibilità del prodotto che riceverà in input la quantità ed uno per la lettura delle informazioni relative al prodotto che restituirà l’entità Prodotto.
Quanto visto rispetta il principio CQS, ma il modello CQRS si spinge oltre introducendo la segregazione a livello architetturale che coinvolge tutto lo stack applicativo: componenti, servizi, database e modelli di dati separati per il lato del comando e il lato della query. In altri termini sono identificati due flussi dati completamente separati, uno di lettura ed uno di scrittura, che vanno ad agire su database distinti. La separazione delle basi dati presenta diversi vantaggi:
- E’ possibile utilizzare tecnologie differenti, ad esempio possiamo utilizzare un database relazionale per la scrittura dei dati ed un no-SQL database per la parte di lettura.
- Spesso le operazioni di lettura sono maggiori di quelle di lettura e conseguentemente può avere senso scalare differentemente i due flussi.
- Il modello è applicabile in modo incrementale anche ad applicazioni già esistenti.
Per contro, la separazione degli archivi introduce un problema di sincronismo tra le basi dati che richiede l’utilizzo di strategie apposite.
Event Sourcing
La propagazione degli eventi tra il lato del command e il lato della query è un componente fondamentale del modello CQRS. Ogni operazione di scrittura deve generare un nuovo evento che viene propagato al lato della query al fine di aggiornare le viste dei dati. Allo scopo si può utilizzare la tecnica dell’Event Sourcing, che prevede la memorizzazione di ogni evento in uno store denominato Event Store. Tramite la sequenza degli eventi memorizzati è possibile ricostruire lo stato dell’applicazione in un certo istante.
Nell’utilizzo di tale tecnica è fondamentale che gli eventi siano immutabili, non cancellabili, con una precisa connotazione temporale e contenenti tutte le informazioni necessarie alla loro interpretazione, come ad esempio l’azione di business che li ha generati. Inoltre devono poter essere generati da un unico Publisher ma possono essere letti da più Subscriber.