Introduction to CQRS (Command Query Responsibility Segregation)

Preview:

DESCRIPTION

This presentation is about Command Query Responsibility Segregation (CQRS), a small tactical pattern dealing with different optimization strategies for reads and writes. In other words, CQRS suggests separate models depending whether there is a Command or a Query. Presentation by Oleksandr Loktyev (Team Lead, GlobalLogic, Lviv), delivered at Lviv .Net TechTalk, August 20, 2014. More details - http://www.globallogic.com.ua/press-releases/lviv-dotnet-techtalk-coverage

Citation preview

©2014 GlobalLogic Inc. CONFIDENTIAL

2 CONFIDENTIAL

Introduction to CQRS

Oleksandr Loktyev

3 CONFIDENTIAL

Command-query separation (CQS)

Introduction to CQRS

A method should either change the state of an object or return a result, but not both.

1. Queries. Return results.

2. Commands. Change state.

Pros

• Method intents are well understood

• No side effects

4 CONFIDENTIAL

Command query responsibility segregation (CQRS)

Introduction to CQRS

• A pattern based on CQS principle.

• Conceptual model is split into separate models depending whether it is a Command or Query.

5 CONFIDENTIALIntroduction to CQRS

CustomerService

Customer GetCustomer(CustomerId) CustomerSet GetCustomersWithName(Name) void CreateCustomer(Customer) void EditCustomerDetails(CustomerDetails)

Applying CQRS on this would result in two services:

CustomerServiceCommands

void CreateCustomer(Customer) void EditCustomerDetails(CustomerDetails)

CustomerServiceQueries

Customer GetCustomer(CustomerId) CustomerSet GetCustomersWithName(Name) 

6 CONFIDENTIALIntroduction to CQRS

And that’s it!

There is nothing more to it than that… 

7 CONFIDENTIAL

Typical web architecture

Introduction to CQRS

8 CONFIDENTIAL

Domain objects are natural for CrUD operations

Introduction to CQRS

public void Undismiss(DismissedObjectDTO dto)

{

var dismissedObject = _uow.DismissedObjectRepository.Get (dto.ObjectId, dto.ObjectTypeId);

dismissedObject.EndDate = DateTime.Now;

_uow.DismissedObjectRepository.Save(dismissedObject);

if (dismissedObject.ObjectType.ID == (int) Enums.CoachingObjectType.Observation)

{

var reviewDetail = _uow.EventReviewDetailDAO.Get(dto.ObjectId);

erd.ObservationScore = 100;

_uow.EventRepository.Save(erd);

}

_uow.CommitChanges();

}

9 CONFIDENTIAL

But can be extremely inefficient for reads

Introduction to CQRS

Lazy loading:

public Company GetCameraCompany(Camera camera)

{

return camera.Vehicle.Driver.Company;

}

10 CONFIDENTIAL

One more example

Introduction to CQRS

Incorrect fetching strategy.

private static void SomeOperation(List<Customer> customers)

{

foreach (var customer in customers)

{

foreach (var order in customer.Orders)

{

// do something

}

}

}

11 CONFIDENTIAL

Something that is more natural for Read operation

Introduction to CQRS

Plain data instead of Domain Model.

private static void SomeOperation(List<Customer> customers)

{

var customerOrderDTO = _uow.customerRepository.GetCustomerOrders(customers);

foreach (var order in customerOrderDTO .Orders)

{

// do something

}

}

12 CONFIDENTIAL

Queries

Introduction to CQRS

• What’s the purpose of queries? To show data. Not objects.

• So why should the data from the database come across 5 layers through 3 model transformations? It’s a bit overkill to display data.

• Why not just this: The UI read data from the database and displays it?

13 CONFIDENTIALIntroduction to CQRS

Consequence 1

Commands and queries use the same data but they should not necessarily use the same data model

14 CONFIDENTIAL

CQRS way

Introduction to CQRS

It is natural to use a different model to update information than the model you use to read information.

15 CONFIDENTIAL

Simple query for grid

Introduction to CQRS

SELECT

c.Name, o.Name, ot.Type

FROM Customer c

INNER JOIN CustomerOrder co ON co.CustomerId = c.CustomerId

INNER JOIN Order o ON co.OrderId = c.OrderId

INNER JOIN OrderType ot ON o.OrderId = ot.OrderId

WHERE

o.CustomerId = 20

AND ot.Type = ‘Active’

16 CONFIDENTIAL

What if the query is slow?

Introduction to CQRS

1. Execution plan and query optimization.

2. Add indexes.

3. Move to stored procedure.

4. Completely remove ORM.

The fact is that some queries simply cannot be quick.

17 CONFIDENTIAL

Let’s add denormalization

Introduction to CQRS

SELECT

CustomerName, OrderName, OrderType

FROM CustomerOrderView co

WHERE

CustomerId = 20

AND OrderType = ‘Active’

18 CONFIDENTIALIntroduction to CQRS

Consequence 2

Database can contain denormalized data for reads

19 CONFIDENTIAL

CQRS way

Introduction to CQRS

Queries tend to use denormalized data rather then classical database approach.

20 CONFIDENTIAL

Let’s go further and separate storages

Introduction to CQRS

21 CONFIDENTIAL

Read storage can be actually anything you want

Introduction to CQRS

22 CONFIDENTIAL

How to populate read storage? Synchronous way.

Introduction to CQRS

23 CONFIDENTIAL

How to populate read storage? Async way.

Introduction to CQRS

24 CONFIDENTIAL

CQRS is …

Introduction to CQRS

• CQRS is a small tactical pattern

• CQRS is learnable in 5 minutes

• CQRS can open many doors

25 CONFIDENTIALIntroduction to CQRS

Benefits• Handles domain complexity

• Applies different optimization strategies for reads and writes

• Scalability

When to use• Complex domains

• High performance applications

• Only on specific portions of a system (Bounded Context in DDD) 

26 CONFIDENTIAL

Questions?

Recommended