cqrs and what it means for your architecture
TRANSCRIPT
CQRS Separating your peas from your
potatoes
http://richard-banks.org, @rbanks54
CommandQuery
ResponsibilitySeparation
Commands– update state, no return value
Queries– read state, return data
- no side effects
Example: HTTP Verbs
Queries---
GETHEAD
OPTIONSTRACE
Commands---
PUTPOSTPATCH
DELETE
By the way,
is it CQS or CQRS?
CQS = Command Query Separation
In one class, have separate methods for changing and reading state
public class UserAccount{ //...
public bool IsActive { get; private set; }
public void Activate() { IsActive = true; }
public void DeActivate() { IsActive = false; }}
Query
Command
Command
CQRS
Have separate classes for changing and reading state
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; }}
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
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..."); }}
Possible first reaction?Whoa! Lots more code!!
Yes. Though it’s more expressive and intentional too.
Question:
Where should persistence calls occur?
MVC/API Controller?
In the UserAccount class?
Elsewhere?
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..."); }}
Can we use query objects instead of query services? Sure!
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(); }}
Question:
Should we drop the .Activate() and .DeActivate() methods from
the UserAccount class?
public class UserAccount{ //Hello, anaemic domain model!
public bool IsActive { get; internal set; }}
Is an Anaemic Domain Model OK?
Consultant answer: It Depends!
Consider:
Are we building a CRUD app?
What happens when business rules change?
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
Design Principles
Separation of Concerns,Single Responsibility
Domain Classes are responsible for their behaviours
(Avoid the anaemic domain model)
Command Handlers are responsible for coordinating state
changes
(use repositories to persist those changes)
Query Objects are responsible for retrieving data in a shape the
consumer wants.
(Completely avoids calling the domain)
What’s this mean for my architecture?
UI Application Domain Data
Commands
UI Application Data
Queries
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
Commands
A command should describea single user intention
against a single domain entity
e.g. OpenAccount,ResetAccountPassword
DisableAccount
Thinking about commands and objects leads us towards…
Domain Driven Design
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’)
Define modules in your app based on your Domain Contexts.
Commands act on theAggregate Roots in your Context
Queries
Queries just need to get data.
So why go via the domain model?
UI Application Domain Data
Commands
UI Application Data
Queries
Why not de-normalise our datato optimise for reads?
Why not store it in a different database?
Question:
If we split the storage of our domain data and our denormalised data…
How do we keep things in sync?
We could keep the data on the same DB server…
…and use transactions/triggers to update multiple tables at
once.
Or we could think about eventual consistency.
(Note: This is not required for CQRS)
When a Domain Entity changes state, raise a Domain Event to
describe what happened.
These Domain Events can be treated as messages and
published on a message bus
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.
Publishing Domain Events can lead to thinking about Event
Sourcing.
Note: Event Sourcing is NOT REQUIRED for CQRS
Commands and Queries will likely lead to a Task based UI approach.
What’s the catch?
CQRS means you’ll need to think about design and your domain
model.
It’s not needed for CRUDdy, “forms over data” applications
Commands are not “fire and forget”.
You need to think about concurrency and exception
handling.
You need to decide how to denormalize data.
Eventual consistency is not simple.
It’s easy to fall back into old habits
It’s hard to add to a legacy system
It’s complexity you might not need.
Apply the YAGNI rule.
So Why Use It Then?
You have a complex domain model
You have a scalability or performance problem
You’re building a task based UI
You have a collaborative domain model
Think, concurrent partial updates of the same entity by multiple people
OK. We’re Done!
Want some references?
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/