cqrs and what it means for your architecture

Post on 16-Apr-2017

1.569 Views

Category:

Software

3 Downloads

Preview:

Click to see full reader

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/

top related