cqrs and what it means for your architecture
TRANSCRIPT
![Page 1: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/1.jpg)
CQRS Separating your peas from your
potatoes
http://richard-banks.org, @rbanks54
![Page 2: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/2.jpg)
CommandQuery
ResponsibilitySeparation
![Page 3: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/3.jpg)
Commands– update state, no return value
Queries– read state, return data
- no side effects
![Page 4: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/4.jpg)
Example: HTTP Verbs
![Page 5: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/5.jpg)
Queries---
GETHEAD
OPTIONSTRACE
![Page 6: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/6.jpg)
Commands---
PUTPOSTPATCH
DELETE
![Page 7: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/7.jpg)
By the way,
is it CQS or CQRS?
![Page 8: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/8.jpg)
CQS = Command Query Separation
In one class, have separate methods for changing and reading state
![Page 9: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/9.jpg)
public class UserAccount{ //...
public bool IsActive { get; private set; }
public void Activate() { IsActive = true; }
public void DeActivate() { IsActive = false; }}
Query
Command
Command
![Page 10: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/10.jpg)
CQRS
Have separate classes for changing and reading state
![Page 11: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/11.jpg)
public class UserAccount{ //Only change state of the object via methods within the class //State changing methods are marked internal
public bool IsActive { get; private set; }
internal void Activate() { IsActive = true; }
internal void DeActivate() { IsActive = false; }}
![Page 12: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/12.jpg)
public class AccountQueryService{ IQueryable<UserAccount> db; public AccountQueryService(IQueryable<UserAccount> db) { this.db = db; }
public UserAccount FindByName(string accountName) { return db.Where(a => a.Name.Equals(accountName)).Single(); }
public IEnumerable<UserAccount> ActiveAccounts() { return db.All(a => a.IsActive); }}
Anti Pattern in use for simplicity of sample code
Should return just the data needed, not a domain object
![Page 13: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/13.jpg)
public class AccountCommandHandler{ AccountQueryService view; ILogger logger;
public AccountCommandHandler(AccountQueryService view, ILogger logger) { this.view = view; this.logger = logger; }
public void ActivateAccount(string accountName) { try
{ var account = view.FindByName(accountName); account.Activate(); } catch { logger.Log("oops!"); throw; } logger.Log("Account activated..."); }}
![Page 14: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/14.jpg)
Possible first reaction?Whoa! Lots more code!!
Yes. Though it’s more expressive and intentional too.
![Page 15: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/15.jpg)
Question:
Where should persistence calls occur?
![Page 16: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/16.jpg)
MVC/API Controller?
In the UserAccount class?
Elsewhere?
![Page 17: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/17.jpg)
public class AccountCommandHandler{ //… public void ActivateAccount(string accountName) { try { var account = view.FindByName(accountName); account.Activate(); db.Save(account); } catch { logger.Log("oops!"); throw; } logger.Log("Account activated..."); }}
![Page 18: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/18.jpg)
Can we use query objects instead of query services? Sure!
![Page 19: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/19.jpg)
public class ActiveAccountsQuery : IQuery{ IQueryable<UserAccount> db; public ActiveAccountsQuery(IQueryable<UserAccount> db) { this.db = db; }
public IEnumerable<UserAccount> Execute() { return db.All(a => a.IsActive).ToList(); }}
![Page 20: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/20.jpg)
Question:
Should we drop the .Activate() and .DeActivate() methods from
the UserAccount class?
![Page 21: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/21.jpg)
public class UserAccount{ //Hello, anaemic domain model!
public bool IsActive { get; internal set; }}
![Page 22: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/22.jpg)
Is an Anaemic Domain Model OK?
Consultant answer: It Depends!
![Page 23: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/23.jpg)
Consider:
Are we building a CRUD app?
What happens when business rules change?
![Page 24: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/24.jpg)
public class UserAccount{ private string EmailValidated { get; private set; } public bool IsActive { get; private set; }
internal void Activate() { if (!EmailValidated) throw new ApplicationException("email is not yet verified"); IsActive = true; }
internal void DeActivate() { IsActive = false; }}
Behaviour change implemented in the
domain entity
No changes needed anywhere else
![Page 25: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/25.jpg)
Design Principles
![Page 26: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/26.jpg)
Separation of Concerns,Single Responsibility
![Page 27: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/27.jpg)
Domain Classes are responsible for their behaviours
(Avoid the anaemic domain model)
![Page 28: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/28.jpg)
Command Handlers are responsible for coordinating state
changes
(use repositories to persist those changes)
![Page 29: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/29.jpg)
Query Objects are responsible for retrieving data in a shape the
consumer wants.
(Completely avoids calling the domain)
![Page 30: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/30.jpg)
What’s this mean for my architecture?
![Page 31: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/31.jpg)
UI Application Domain Data
Commands
UI Application Data
Queries
![Page 32: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/32.jpg)
Command Services/Handlers
UI
Commands
Domain Model
Repositories
DatabaseQuery Store
Query Service
Data Access Layer
Projections
Queries Business Actions
Changes
Query Store
No O/R conversions
Describes the user’s
intent
Optimized for querying
Synchronous or asynchronous
updates Can simply be
denormalised tables in the main DB
![Page 33: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/33.jpg)
Commands
![Page 34: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/34.jpg)
A command should describea single user intention
against a single domain entity
e.g. OpenAccount,ResetAccountPassword
DisableAccount
![Page 35: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/35.jpg)
Thinking about commands and objects leads us towards…
Domain Driven Design
![Page 36: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/36.jpg)
High level DDD Concepts:
Entity: discrete lifecycle and identity
Value Object: no lifecycle, identified only by attribute values.
Aggregate: one or more entities and value objects,clustered together as a coherent whole.
Agg. Root: single entity that controls access to otherobjects in the aggregate
Context: setting in which a word has specific meaning (e.g ‘account’)
![Page 37: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/37.jpg)
Define modules in your app based on your Domain Contexts.
![Page 38: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/38.jpg)
Commands act on theAggregate Roots in your Context
![Page 39: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/39.jpg)
Queries
![Page 40: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/40.jpg)
Queries just need to get data.
So why go via the domain model?
![Page 41: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/41.jpg)
UI Application Domain Data
Commands
UI Application Data
Queries
![Page 42: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/42.jpg)
Why not de-normalise our datato optimise for reads?
Why not store it in a different database?
![Page 43: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/43.jpg)
Question:
If we split the storage of our domain data and our denormalised data…
How do we keep things in sync?
![Page 44: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/44.jpg)
We could keep the data on the same DB server…
…and use transactions/triggers to update multiple tables at
once.
![Page 45: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/45.jpg)
Or we could think about eventual consistency.
(Note: This is not required for CQRS)
![Page 46: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/46.jpg)
When a Domain Entity changes state, raise a Domain Event to
describe what happened.
![Page 47: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/47.jpg)
These Domain Events can be treated as messages and
published on a message bus
![Page 48: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/48.jpg)
Event Handlers subscribe to the events and update denormalised
views of data.
Makes transactions simpler.Must consider the impact of Eventual
Consistency on our users.
![Page 49: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/49.jpg)
Publishing Domain Events can lead to thinking about Event
Sourcing.
Note: Event Sourcing is NOT REQUIRED for CQRS
![Page 50: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/50.jpg)
Commands and Queries will likely lead to a Task based UI approach.
![Page 51: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/51.jpg)
What’s the catch?
![Page 52: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/52.jpg)
CQRS means you’ll need to think about design and your domain
model.
![Page 53: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/53.jpg)
It’s not needed for CRUDdy, “forms over data” applications
![Page 54: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/54.jpg)
Commands are not “fire and forget”.
You need to think about concurrency and exception
handling.
![Page 55: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/55.jpg)
You need to decide how to denormalize data.
Eventual consistency is not simple.
![Page 56: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/56.jpg)
It’s easy to fall back into old habits
![Page 57: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/57.jpg)
It’s hard to add to a legacy system
![Page 58: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/58.jpg)
It’s complexity you might not need.
Apply the YAGNI rule.
![Page 59: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/59.jpg)
So Why Use It Then?
![Page 60: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/60.jpg)
You have a complex domain model
![Page 61: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/61.jpg)
You have a scalability or performance problem
![Page 62: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/62.jpg)
You’re building a task based UI
![Page 63: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/63.jpg)
You have a collaborative domain model
Think, concurrent partial updates of the same entity by multiple people
![Page 64: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/64.jpg)
OK. We’re Done!
Want some references?
![Page 65: CQRS and what it means for your architecture](https://reader035.vdocuments.us/reader035/viewer/2022062905/5871233b1a28abe4448b5895/html5/thumbnails/65.jpg)
MS Patterns & Practices, CQRS Journey: http://cqrsjourney.github.io/
Greg Youngs CRQS and Event Sourcing demo app: https://github.com/gregoryyoung/m-r
NCQRS Framework: https://github.com/pjvds/ncqrs
CQRS FAQ: http://www.cqrs.nu/Faq/command-query-responsibility-segregation
Effective Aggregate Design: http://dddcommunity.org/library/vernon_2011/
CQRS Myths: https://lostechies.com/jimmybogard/2012/08/22/busting-some-cqrs-myths/
Task based UIs: https://cqrs.wordpress.com/documents/task-based-ui/