You have the dream situation - the ability to create a brand new product from the ground up. You want it to be scalable, performant and easily updatable (since you're building in an incremental fashion). But since you're a start-up, you don't want to deal with the headache and costs of hosting. Well, one possibility is taking advantage of cloud platforms (Windows Azure, for instance) and architecting your system using a CQRS-style pattern. See how you can create a system patterned on CQRS in the cloud. We can take a look at a product I created (BrainCredits) and also see how others would take advantage of Azure services to create CQRS-patterned systems.
2. About Me
C# MVP (since April 2011)
Co-Founder of BrainCredits (braincredits.com)
Principal at AgileWays
President of the Pittsburgh .NET User Group
(meetup.com/pghdotnet) and organizer of PGH
TechFest
Twitter - @DavidHoerster
Blog – http://geekswithblogs.net/DavidHoerster
Email – david@braincredits.com
5. What is CQRS?
Command Query Responsibility Segregation
Based upon Bertrand Meyer‟s Command Query
Separation (CQS) principle:
“every method should either be a command that
performs an action, or a query that returns data to the
caller, but not both. In other words, asking a question
should not change the answer” (Meyer)
In essence, commands return acknowledgement;
queries return data.
6. Backstory
Goals for BrainCredits.com Product
Fast reads
Scalable
Handle, potentially, large volumes of requests
Be able to change quickly
Ability to track user in system and events that
occur
Easy to deploy, manage
Inexpensive (FREE????)
7. Backstory
Decided to jump into cloud with Azure
Scalability
Deployment via VS2010 (Familiarity)
Pretty inexpensive – no HW to manage
BizSpark company (at the time)
– Currently 1500 compute hours of small instance
– 5 GB of SQL Azure
– Small cache instance
– 2 million transactions/mo
– and more…
But how to store data and design system?
8. Backstory
I would recommend to start looking along
the CQRS style of architectures
- Rinat Abdullin
Articles by Greg Young, Udi
Dahan, J. Oliver, R. Abdullin
9. What is CQRS?
Common Architecture Issues
Business logic bleeds through layers
Validation logic ends up everywhere
Copy and Paste development
Apps that cater to modification ops
Leads to general complexity within tiers
CQRS simplifies this with classes
performing specific duties (SRP)
10. Evolution of a System to CQRS
Simple App (tightly coupled)
Application
Database
11. Evolution of a System to CQRS
Breaking out business logic
Application
Business Logic
Database
WriteData(string, string, int, bool, …) { }
12. Evolution of a System to CQRS
Introduce messaging
MSG
Application Business Service
Database
13. Evolution of a System to CQRS
Query Side Client Command Side
Data Requested/Returned Command Issued – ack/nack
Repository Command Service
Domain (ARs)
Optional
Event Handler
Denormalizer Event Store
Read Model Store
14. What is CQRS?
Encapsulates business logic in the domain
Allows you to model tasks instead of CRUD
Allows for scalability very easily
Can provide for fast reads at the „expense‟ of
more expensive writes
Once the data is read by the client, it‟s already
old
A system that is eventually consistent is usually
acceptable
EVENTUAL CONSISTENCY!
– The read model for the system eventually is consistent
15. What is CQRS?
Taking this further, CQRS describes a
pattern where
Commands are messages (possibly
asynchronous)
Queries go against a read model
Since commands are returning void, it
allows for the message to be transformed
into the read model
This flips the conventional wisdom of fast
writes and slow reads
17. High-Level Code Walk-through of
CQRS
Command
A message sent to tell the system to do something
public class CreateReservation : CommandBase
{
public Guid ReservationId { get; set; }
public String Name { get; set; }
public Int32 NumberOfSeats { get; set; }
public String DiscountCode { get; set; }
}
18. High-Level Code Walk-through of
CQRS
Domain
Your system‟s business logic and raises events
public class Reservation : AggregateRootBase {
public void CreateNewReservation(CreateReservation cmd) {
//do some logic to figure out TotalCost
ApplyEvent(new ReservationCreated() {
ReservationId = cmd.ReservationId,
ReservationMade = DateTime.UtcNow,
NumberOfSeats = cmd.NumberOfSeats,
SeatsReservedFor = cmd.Name,
TotalCost = totalCost
});
}
19. High-Level Code Walk-through of
CQRS
Events
Messages raised by the domain stating that
something has been done
public class ReservationCreated : EventBase {
public Guid ReservationId { get; set; }
public DateTime ReservationMade { get; set; }
public Int32 NumberOfSeats { get; set; }
public String SeatsReservedFor { get; set; }
public Decimal TotalCost { get; set; }
}
20. High-Level Code Walk-through of
CQRS
Event Handlers (Denormalizers)
Persist event information to the read model
public void Handle(ReservationCreated theEvent) {
var reservation = new Reservation() {
IsDeleted = false,
NumberOfSeats = theEvent.NumberOfSeats,
ReservationId = theEvent.ReservationId,
ReservationMade = theEvent.ReservationMade,
SeatsReservedFor = theEvent.SeatsReservedFor,
TotalCost = theEvent.TotalCost };
var repo = new ReservationRepository();
repo.CreateReservation(reservation);
}
21. Benefits
Get all of the orders for user “David” in last
30 days SalesOrderHeader (SalesLT)
SalesOrderID
RevisionNumber
SalesOrderDetail (SalesLT)
SalesOrderID
SalesOrderDetailID
OrderQty
OrderDate
ProductID
DueDate Product (SalesLT)
UnitPrice
ShipDate ProductID
UnitPriceDiscount
Status Name
LineTotal
OnlineOrderFlag ProductNumber
rowguid
SalesOrderNumber Color
ModifiedDate
PurchaseOrderNumber StandardCost
AccountNumber ListPrice
CustomerID Size
ShipToAddressID Weight
BillToAddressID ProductCategoryID
ShipMethod ProductModelID
CreditCardApprovalCode SellStartDate
SubTotal SellEndDate
TaxAmt
Freight
Customer (SalesLT) ProductModel (SalesLT) ProductCategory (SalesLT)
CustomerID ProductModelID ProductCategoryID
NameStyle Name ParentProductCategoryID
Title CatalogDescription Name
FirstName rowguid rowguid
MiddleName ModifiedDate ModifiedDate
LastName
Suffix
CompanyName
SalesPerson
EmailAddress
22. Benefits
Get all the orders for user „David‟ in last 30
days
SELECT c.FirstName, c.MiddleName, c.LastName, soh.SalesOrderID, soh.OrderDate,
sod.UnitPrice, sod.OrderQty, sod.LineTotal,
p.Name as 'ProductName', p.Color, p.ProductNumber,
pm.Name as 'ProductModel',
pc.Name as 'ProductCategory',
pcParent.Name as 'ProductParentCategory'
FROM SalesLT.Customer c INNER JOIN SalesLT.SalesOrderHeader soh
ON c.CustomerID = soh.CustomerID
INNER JOIN SalesLT.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID
INNER JOIN SalesLT.Product p ON sod.ProductID = p.ProductID
INNER JOIN SalesLT.ProductModel pm ON p.ProductModelID = pm.ProductModelID
INNER JOIN SalesLT.ProductCategory pc ON p.ProductCategoryID = pc.ProductCategoryID
INNER JOIN SalesLT.ProductCategory pcParent ON pc.ParentProductCategoryID =
pcParent.ProductCategoryID
WHERE c.FirstName = 'David'
AND soh.OrderDate > (GETDATE()-30)
23. Benefits
Wouldn‟t it be great if it was something like?
SELECT
FirstName, MiddleName, LastName, SalesOrderID, OrderDat
e, UnitPrice, OrderQty, LineTotal, ProductName, Color, Produ
ctNumber, ProductModel, ProductCategory, ProductParentCa
tegory
FROM CustomerSales
WHERE FirstName = 'David'
AND OrderDate > (GETDATE()-30)
24. Command Side Flow
Event 1 Handler 1
Command Domain Event 2 Handler 2 Read
Model
Event N Handler N
25. Read Side Flow
Request for Data
Query
Read Provider
Client
Model (Repo/WCF
DS/etc) DTO
27. CQRS in the Cloud
Hosting CQRS in Azure
Typically, you‟d want 2 roles
Web Role for the UI, which also performs
querying
Worker Role for the Command handling
Both roles can scale out as much as needed
Multiple web roles with a single worker role, or
vice versa or both!
28. CQRS in Azure
Query Side
Web Role Command Side
Client
Worker Role
Command Service
Repository
Domain (ARs)
SQL Azure
or other Event Handler
Repository
Denormalizer Event Store
Read Model Store
Table Storage?
29. CQRS in the Cloud
How do I issue commands from my
web role?
Some typical solutions
WCF service on the worker role side
3rd party service bus tool
Use Azure storage features
(blobs, queues, tables)
All have advantages and disadvantages
30. CQRS in the Cloud
General flow is this
Web role issues a command by dropping it in a
blob container (name is GUID).
Command blob name is then dropped in a queue
Worker role monitors queue for work
When it finds a message in the queue, it grabs
blob from storage based on id in queue message
Deserializes command and then sends it through
normal CQRS pipeline
31. CQRS in the Cloud
Web Role Worker Role
Blob Storage
Queue
Command
Controller Domain
Handler
Azure Table
Storage Event(s)
Repository
Denormalizer
Azure Sub-system
Read Model
32. CQRS in the Cloud
Web and Worker Role Separation
Share knowledge of queues and interface
If worker were to go down, web would still
run
Commands would queue up
Provides some level of fault tolerance
33. CQRS in the Cloud
Worker role can spawn multiple threads to
monitor queues
BrainCredits has several threads
CommandQueue and LogQueue
Different polling intervals
Depending on importance of information
34. CQRS in the Cloud
Using Azure Table Storage for logging user
actions…
Different than events. Want to know user
behavior.
Storage is huge – just watch transaction costs
Consider batching log reads
But you can create replays of user interaction
Similar to event sourcing‟s replay of events
Event sourcing doesn‟t capture “Cancels” or “Reads”,
which may be useful from a marketing perspective
35. Claim Credit for this Session
BrainCredits
http://www.braincredits.com/Lesson/17210
36. Resources
Cerebrata Cloud Storage Studio – http://cerebrata.com
Paraleap AzureWatch – http://paraleap.com
Conference Reservation Example on GitHub
https://github.com/DavidHoerster/Conference
CQRS Journey – https://github.com/mspnp/cqrs-journey-doc
CQRS Info Site – http://cqrs.wordpress.com/
DDD/CQRS Google Group -
http://groups.google.com/group/dddcqrs
Udi Dahan’s CQRS article “Clarified CQRS” –
http://www.udidahan.com/2009/12/09/clarified-cqrs/
Rinat Abdullin’s CQRS information – http://abdullin.com/cqrs
Distributed Podcast - http://distributedpodcast.com/