layered expression trees feat. cqrs
DESCRIPTION
CQRS, ovvero: 2 stack, uno per "leggere" e l'altro per "scrivere". Se per "scrivere" abbiamo l'imbarazzo della scelta (Domain Model, Command, Event Sourcing, ...) per leggere, invece, apparentemente c'è poco da dire. "Apparentemente", appunto. Parliamone :-)TRANSCRIPT
Layered Expression Trees feat. CQRS
Andrea Saltarello C.T.O. @ managed/designs http://blogs.ugidotnet.org/pape http://twitter.com/andysal74
http://creativecommons.org/licenses/by-nc-nd/2.5/
Andrea Saltarello! Chi era costui?
• C.T.O. di Managed Designs, alla ricerca della «sostenibilità dello sviluppo software» 1. Il cliente «deve» essere soddisfatto e pagare 2. Il fornitore «deve» avere un margine ragionevole 3. Il team «deve» essere soddisfatto del proprio lavoro
• (Co)Fondatore e presidente di UGIdotNET: ho bisogno di condividere esperienze a causa del bullet precedente
• (Co)Fondatore e collo di bottiglia di GUISA • (Co)Autore di .NET: Architecting Applications
for the Enterprise di Microsoft Press • Il #NAAE è il mio giornale di bordo, nel quale (nel
2008) ho cercato di «sintetizzare» i punti 1 e 2
In realtà…
Andrea Saltarello è l’amico di penna di @ayende
«BLUE» DDD
Blue Book: Architettura (1/2)
Blue Book: Architettura (2/2)
è una layered architecture
i layer Presentation e Infrastructure compaiono «per sport» nel diagramma
i layer Application e Domain costituiscono quella che tipicamente chiamiamo «business logic»
Domain: logica invariante rispetto ai casi d’uso
Application: logica specifica ai casi d’uso
Domain layer: Key Concepts
Il Domain Layer contiene la domain logic ed è composto da
Model: Entità (identità e stato) e Valori (solo stato)
Servizi
Il Model è «both data and behaviour» (cit.)
La persistenza del Model è gestita da Repository
Le istanze delle entità di dominio sono costruite da Factory
Entità e Valori a runtime formano dei grafi di oggetti. I grafi dotati di “dignità propria” sono chiamati Aggregate e il sistema (es: i Repository) si “impegna” a gestirli correttamente ed atomicamente
Application Layer: in teoria
Application Layer: defines the jobs the software is supposed to do and directs the expressive domain objects to work out problems. The tasks this layer is responsible for are meaningful to the business or necessary for interaction with the application layers of other systems. This layer is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down. It does not have state reflecting the business situation, but it can have state that reflects the progress of a task for the user or the program.
Application Layer: in pratica
E’ un catalogo di servizi in grado di effettuare il mesh della logica presente nel domain layer esponendola alla applicazione (es: presentation layer) in una forma ad… Alta digeribilità.
CQRS
Command Query Responsibility Segregation
Command and Query Responsibility Segregation (CQRS) originated with Bertrand Meyer’s Command and Query Separation Principle. Wikipedia defines the principle as:
It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both.
Command and Query Responsibility Segregation uses the same definition of Commands and Queries that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is that in CQRS objects are split into two objects, one containing the Commands one containing the Queries.
[Fonte: http://cqrsinfo.com/documents/cqrs-introduction/ ]
Command Query Responsibility Segregation
Query Command
CQRS, a.k.a. «Stato di Diritto»
CQRS introduce in DDD la «separazione dei poteri»
• 1 stack per «leggere»
• 1 stack per «eseguire»
In pratica, significa non dover «indovinare» un modello valido per entrambe le esigenze
In questa sessione ci interessa la «Q»
Scenario 1: e-commerce
Home page: visualizzare prodotti raccomandati
Considerati tutti i prodotti catalogati, dall’elenco di quelli in vendita presentare i 3 aventi maggior giacenza in magazzino.
Ergo:
• Prodotti catalogati->Prodotti in vendita (domain)
• Prodotti in vendita->3 prodotti aventi maggior giacenza (application)
Scenario 2: ERP
Visualizzare fatture insolute di una business unit
Considerate tutte le fatture, estrarre quelle attinenti una business unit e tra esse visualizzare tutte quelle scadute e non pagate a distanza di 30 gg dalla scadenza
Ergo:
• Fatture emesse -> Fatture della BU
• Fatture BU -> Fatture scadute
• Fatture scadute -> Fatture insolute
Scenario 3: CMS
Visualizzare gli ultimi 5 articoli
Considerati tutti gli articoli, dall’elenco di quelli pubblicati estrarre i 5 più recenti
Ergo:
• Articoli -> Articoli pubblicati
• Articoli pubblicati -> 5 articoli più recenti
Scenario 4: CMS (Reloaded)
Ricerca articolo
Considerati tutti gli articoli, dall’elenco di quelli pubblicati estrarre quelli che soddisfano i criteri di ricerca
Ergo:
• Articoli -> Articoli pubblicati
• Articoli pubblicati -> articoli che soddisfano la query
Scenario 5: e-commerce (strikes back)
Ricerca prodotti
Considerati tutti i prodotti catalogati, dall’elenco di quelli in vendita estrarre quelli che soddisfano i criteri di ricerca.
Ergo:
• Prodotti catalogati->Prodotti in vendita
• Prodotti in vendita-> prodotti che soddisfano la query
La «Q» in CQRS
Cosa hanno in comune questi casi?
• Ogni «filtro» è una regola che «lavora» sul grafo
• Composizione di regole
• Ogni regola può essere usata in differenti parti dell’applicazione
Come fare… Senza un «botto» di DTO?
P.S.: Si, «botto» è un ordine di grandezza del S.I. Kilo->Mega->Giga->Tera->Peta->Botto
Layered Expression Trees (LET idiom)
Facciamo un gioco: facciamo che layer e servizi si scambino degli IQueryable<YourFavouriteDomainEntity> facendo «emergere» la query e specificando la proiezione solo all’ultimo momento?
LET idiom feat. Ubiquitous Language
LET aumenta la pervasività dell’Ubiquitous Language nel codice:
recommendedProducts = (from p in
CatalogServices.GetAvailableProductsOnSale()
orderby p.UnitsInStock descending
select new ProductDescriptor
{
Id = p.Id,
Name = p.Name,
UnitPrice = p.UnitPrice,
UnitsInStock = p.UnitsInStock,
}).Take(3);
Questa query è «quasi» scritta nel linguaggio del Domain Expert
Query Command
LET feat. CQRS
The Good
Ridotto effort di realizzazione/gestione: • Nessun DTO (ad eccezione del ViewModel,
che avremmo comunque) • Modello «database like» facile da ottenere
rapidamente • Strutturazione «DDD like» della logica
• Se il DBMS è SQL Server, funziona «out of the box»
Performance: • Estrazione «all at once» di tutti e soli i dati
necessari
The Bad
Unit testing «acrobatico»:
• In pratica, LINQ 2 Objects (o similari)
• In ogni caso, è un LINQ provider differente :-/ #meh
The Ugly
• NHibernate è praticamente inutilizzabile: il LINQ provider «scoppia»
• Ogni LINQ provider è una storia a sè
One more thing… (cit.)
LET idiom, v2
Se:
• dovessimo realizzare una applicazione e non un framework
• per noi gli idiomi fossero «belli belli belli in modo assurdo» (cit.)
…Allora potremmo rimpiazzare i servizi con degli extension methods ad hoc
LET idiom, v2
public static class OutboundInvoiceExtensions { public static IQueryable<OutboundInvoice> RetrieveExpiredOutboundInvoices(this IQueryable<OutboundInvoice> query) { var expiredInvoices = from i in query.RetrieveUnpaidOutboundInvoices() where i.PaymentDueDate < DateTime.Now select i; return expiredInvoices; } public static IQueryable<OutboundInvoice> RetrieveUnpaidOutboundInvoices(this IQueryable<OutboundInvoice> query) { var unpaidInvoices = from i in query where i.Status == "E" select i; return unpaidInvoices; } public static IQueryable<OutboundInvoice> FilterByBusinessUnit(this IQueryable<OutboundInvoice> query, int businessUnitId) { var invoices = from i in query where i.JobOrder.BusinessUnit.OrganizationID == businessUnitId select i; return invoices; } }
LET2 feat. Fluent Ubiquitous Language
Sembra Specification ma non è, serve a darti l’allegria! (semi cit.)
expiredOutboundInvoices = from i in
ReadModelFacade.OutboundInvoices .FilterByBusinessUnit(businessUnitId)
.RetrieveExpiredOutboundInvoices()
orderby i.PaymentDueDate
select new
SummaryViewModel.OutboundInvoice
{
Id = i.ID,
CustomerName = i.Customer.Name,
PaymentDueDate = i.PaymentDueDate.Value,
TotalAmount = i.TotalPrice,
JobOrderName = i.JobOrder.Name,
Code = i.Code
};
PARLIAMONE
Bibliografia “pratica”
[DDD] Domain Driven Design, Eric Evans , Addison-Wesley
[NAAE] Microsoft .NET: Architecting Applications for the Enterprise, Andrea Saltarello & Dino Esposito, Microsoft Press
CQRS, Task Based UIs, Event Sourcing agh!, Greg Young
[NSK] NSK, http://nsk.codeplex.com
Mio blog ENG, http://blogs.ugidotnet.org/mrbrightside