Tutte le volte che mi capita di parlare con qualcuno di CQRS o di sentire qualcuno parlare di CQRS ho la sensazione che ci si concentri troppo su quella che a mio modo di vedere è la parte meno importante tra le due componenti: i comandi; portare l’architettura verso un modello basato su comandi è fondamentale, sia chiaro, perché ci aiuta a modellare in maniera estremamente più semplice il modello mentale dell’utente, cosa che ci consente di fare un balzo in avanti eccezionale, ma i comandi sono anche la novità del momento e la “C” di CQRS sta oscurando quella che è una parte importantissima di CQRS: Query Responsibility Segregation.

Leggere, leggere e ancora leggere

Anche a scuola ci hanno insegnato che è importantissimo leggere ;-)

Quello che quasi certamente facciamo tutti i giorni, e anche le nostre applicazioni fanno, è nella maggior parte dei casi leggere dei dati, cercare dei dati e analizzare dei dati tutte operazione di lettura che si basano sulla possibilità di avere a disposizione quei dati.

Ma, parafrasando, la domanda è: Quando avete voglia di leggere, a casa vostra, che cosa fate?

Immagino che prendiate un unico gigantesco libro, in cui ci sono mischiate insieme le pagine di tutti i libri della vostra biblioteca, e cominciate a leggere saltando qua e la cercando di capire se la pagina che state leggendo ha attinenza con quella che avete appena finito di leggere…

Assurdo non trovate?

Allora la seconda domanda è: perché lo facciamo fare alle nostre applicazioni cercando di districarci in questo?

crazy

Pigrizia

Ammetto che l’unica buona motivazione che riesco a trovare è la pigrizia, abbiamo in nostro ORM di turno, mappiamo le classi del dominio modello a oggetti (fate un favore a voi stessi e siate onesti: non chiamate il robo la sopra dominio) e lo usiamo con il fidato linq:

var query = from order in session.Orders
                        from item in order.Items
                        where item.Product.Price > price
                                      && item.Quantity < qty
                                      && order.Customer.ShippingAddress.PostalCode == postalCode
                        select new
                        {
                            order.Customer.Name,
                            order.TotalPrice,
                        };

poi lo mettiamo in produzione, casualmente in produzione abbiamo un po’ di dati, sempre casualmente qualcuno si lamenta che è lento e stavolta per puro caso attacchiamo il profiler del nostro RDBMS di fiducia e inorridiamo (non tanto casualmente).

Questione di concetti

Il problema non è la query linq, che per inciso non so neanche se sia formalmente corretta, e neanche la tecnologia scelta per l’ORM e men che meno il database relazionale di turno, il problema è che stiamo cercando di usare un modello pensato per gli ordini per materializzare un modello pensato per le statistiche. Stiamo cioè mischiando quello che in DDD si chiamano Bounded Context e proprio perché sono “bounded” sono isolati e non possono essere mischiati.

La soluzione, al problema di cui sopra, è:

var query = from orderStat in session.OrderStats
                        where orderStat.ProductPrice > price
                                      && orderStat.ItemQuantity < qty
                                      && orderStat.ShippingAddress.PostalCode == postalCode
                        select orderStat;

Passiamo cioè da una join in cui materializziamo una proiezione ad una banale query, come questo venga tecnicamente risolto è un problema che non ci riguarda al momento, quello che è certo è che il “nostro mondo”, la nostra applicazione si è drammaticamente semplificata, aprendoci inoltre ad una pletora di possibili soluzione tecniche per risolvere la query espressa qui sopra, soluzioni che vanno dal mappare la query su una vista nel database e quindi fare tutto sempre con il nostro amato ORM a soluzione estremamente evolute basate su Event Sourcing e le de-normalizzazioni asincrone.

Correva il lontano Aprile 2009…

“molto piccoli e molto tanti” è la conclusione di quel post, riferendosi al fatto che se modelliamo il nostro mondo in tanti componenti molto piccoli otteniamo un modello complesso in cui i componenti semplici e piccoli collaborano per risolvere la complessità.

Il “Query Responsibility Segregation principle” ha lo stesso obiettivo: ridurre all’osso la complessità di ogni singola query facendo si che il modello dati prodotto da ogni singola query sia già nella forma giusta per lo scenario in cui deve essere usato.

La cosa bella è che è molto più semplice di quello che pensiamo, si tratta solo di scrivere un po’ di codice, cosa che del resto è il nostro pane quotidiano, l’altra cosa bella è che nella sua semplicità migliora drasticamente la qualità del nostro prodotto.

.m