Types of CQRS

By Vladimir Khorikov

CQRS is a pretty defined concept. Often, people say that you either follow CQRS or not, meaning that it is some kind of a binary choice. In this article, I’d like to show that there is some wriggle room in this notion and how different types of CQRS can look like.

Type 0: no CQRS

With this type, you don’t have any CQRS whatsoever. That means you have a domain model and you use your domain classes for both serving commands and executing queries.

Let’s say you have a Customer class:

public class Customer

{

public int Id { get; private set; }

public string Name { get; private set; }

public IReadOnlyList<Order> Orders { get; private set; }

public void AddOrder(Order order)

{

/* … */

}

/* Other methods */

}

With the type 0 of CQRS you end up with CustomerRepository class looking like this:

public class CustomerRepository

{

public void Save(Customer customer) { /* … */ }

public Customer GetById(int id) { /* … */ }

public IReadOnlyList<Customer> Search(string name) { /* … */ }

}

Search method here is a query. It is used for fetching customers’ data from database and returning it to a client (a UI layer or a separate application accessing your server through some API). Note that this method returns a list of domain objects.

The advantage of such approach is obvious: it has no code overhead. In other words, you have a single model that you use for both commands and queries and don’t have to duplicate the code at all.

The disadvantage here is that this single model is not optimized for read operations. If you need to show a list of customers in UI, you usually don’t want to display their orders. Instead, you most likely prefer to show only a brief information such as id, name and the number of orders.

The use of a domain class for transferring customers’ data from the database to UI leads to loading all their orders into memory and thus introduces a heavy overhead because UI needs the order count field only, not the orders themselves.

This type of CQRS is good for small applications with little or no performance requirements. For other types of applications, we need to move further.

Type 1: separated class structure

With this type of CQRS, you have your class structure separated for read and write operations. That means you create a set of DTOs to transfer the data you fetch from the database.

The DTO for Customer can look like this:

public class CustomerDto

{

public int Id { get; set; }

public string Name { get; set; }

public int OrderCount { get; set; }

}

The Search method now returns a list of DTOs instead of a list of domain objects:

public class CustomerRepository

{

public void Save(Customer customer) { /* … */ }

public Customer GetById(int id) { /* … */ }

public IReadOnlyList<CustomerDto> Search(string name) { /* … */ }

}

The Search method can use either an ORM or plain ADO.NET to get the data needed. This should be determined by performance requirements in each particular case. There’s no need to fall back to ADO.NET if a method’s performance is good enough.

DTOs introduce some duplication as we need to come up with the same concept twice: once for commands in a form of a domain class and once more for queries in a form of a DTO. But at the same time, they allow us to create clean and explicit data structures that perfectly align with our needs for read operations as they only contain data clients need to display. And the more explicit we are with our code, the better.

I would say that this type of CQRS is sufficient for most of enterprise applications as it gives a pretty good balance between code complexity and performance. Also, with this approach, we have some flexibility in terms of what tool to use for queries. If the performance of a method is not critical, we can use ORM and save developers’ time; otherwise, we may fall back to ADO.NET (or some lightweight ORM like Dapper) and write complex and optimized queries on our own.

If we want to continue separating our read and write models, we need to move further.

Type 2: separated model

This type of CQRS proposes using separated models and sets of API for serving read and write requests.

Type 2 of CQRS

That means that, in addition to DTOs, we extract all the read logic out of our model. Repository now contains only methods that regard to commands:

public class CustomerRepository

{

public void Save(Customer customer) { /* … */ }

public Customer GetById(int id) { /* … */ }

}

And the search logic resides in a separate class:

public class SearchCustomerQueryHandler

{

public IReadOnlyList<CustomerDto> Execute(SearchCustomerQuery query)

{

/* … */

}

}

This approach introduces more overhead comparing to the previous one in terms of code required to handle the complexity, but it is a good solution if you have a heavy read workload.

In addition to the ability to write optimized queries, type 2 of CQRS allows us to easily wrap read portion of API with some caching mechanism or even move read API to another server and setup a load-balancer/failover cluster. It works great if you have a massive disparity between the workload of writes and reads in your system as it allows you to scale the read part of it drastically.

If you need even more performance of read operations, you need to move to type 3 of CQRS.

Type 3: separated storage

That is the type that considered to be the true CQRS by many. To scale read operations even further, we can create a separate data storage optimized specifically for queries we have in our system. Often, such storage might be a NoSQL database like MongoDB or a replica set with several instances of it:

Type 3 of CQRS

The synchronization goes in background mode and can take some time. Such data storages are considered to be eventually consistent.

A good example here could be indexing of customers’ data with Elastic Search. Often we don’t want to use full-text search capabilities built into SQL Server as they don’t scale much. Instead, we could use non-relational data storage optimized specifically for searching customers.

Along with the best scalability for read operations, this type of CQRS brings the highest overhead. Not only should we segregate our read and write model logically, i.e. use different classes and even assemblies for it, but we also need to introduce database-level separation.

Summary

There are different types of CQRS you can leverage in your software; there’s nothing wrong with sticking to the type #1 and not moving further to the types 2 or 3 as long as the type #1 meets your application’s requirements.

I’d like to emphasize this once more: CQRS is not a binary choice. There are some different variations between not separating reads and writes at all (type 0) and separating them completely (type 3).

There should be a balance between the degree of segregation and complexity overhead it introduces. The balance itself should be found in each concrete software application apart, often after several iterations. I strongly believe that CQRS itself should not be implemented “just because we can”; it should only be brought to the table to meet concrete requirements, namely, to scale read operations of the application.

文档引用

http://enterprisecraftsmanship.com/2015/04/20/types-of-cqrs/

Types of CQRS的更多相关文章

  1. CQRS FAQ (翻译)

    我从接触ddd到学习cqrs有6年多了, 其中也遇到了不少疑问, 也向很多的前辈牛人请教得到了很多宝贵的意见和建议. 偶尔的机会看到国外有个站点专门罗列了ddd, cqrs和事件溯源的常见问题. 其中 ...

  2. 手撸一套纯粹的CQRS实现

    关于CQRS,在实现上有很多差异,这是因为CQRS本身很简单,但是它犹如潘多拉魔盒的钥匙,有了它,读写分离.事件溯源.消息传递.最终一致性等都被引入了框架,从而导致CQRS背负了太多的混淆.本文旨在提 ...

  3. OnionArch - 采用DDD+CQRS+.Net 7.0实现的洋葱架构

    博主最近失业在家,找工作之余,看了一些关于洋葱(整洁)架构的资料和项目,有感而发,自己动手写了个洋葱架构解决方案,起名叫OnionArch.基于最新的.Net 7.0 RC1, 数据库采用Postgr ...

  4. DDD CQRS架构和传统架构的优缺点比较

    明天就是大年三十了,今天在家有空,想集中整理一下CQRS架构的特点以及相比传统架构的优缺点分析.先提前祝大家猴年新春快乐.万事如意.身体健康! 最近几年,在DDD的领域,我们经常会看到CQRS架构的概 ...

  5. 谈一下关于CQRS架构如何实现高性能

    CQRS架构简介 前不久,看到博客园一位园友写了一篇文章,其中的观点是,要想高性能,需要尽量:避开网络开销(IO),避开海量数据,避开资源争夺.对于这3点,我觉得很有道理.所以也想谈一下,CQRS架构 ...

  6. AutoMapper:Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 应用场景:ViewModel==>Mode映射的时候出错 AutoMappe ...

  7. 分享一个CQRS/ES架构中基于写文件的EventStore的设计思路

    最近打算用C#实现一个基于文件的EventStore. 什么是EventStore 关于什么是EventStore,如果还不清楚的朋友可以去了解下CQRS/Event Sourcing这种架构,我博客 ...

  8. 一种简单的CQRS架构设计及其实现

    一.为什么要实践领域驱动? 近一年时间我一直在思考一个问题:"如何设计一个松耦合.高伸缩性.易于维护的架构?".之所以有这样的想法是因为我接触的不少项目都是以数据库脚本来实现业务逻 ...

  9. 浅谈命令查询职责分离(CQRS)模式

    在常用的三层架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体.在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复杂,用户增多,这种设计就会出现一些性能 ...

随机推荐

  1. Chrome 控制台console的用法(转)

    下面我们来看看console里面具体提供了哪些方法可以供我们平时调试时使用. 目前控制台方法和属性有: ["$$", "$x", "dir" ...

  2. C#怎么遍历一个对象里面的全部属性?

    比如我现在有一个Student的对象,里面有属性stuName,stuAge,stuGender,我现在该怎么写循环才能遍历这几个属性? Student s=new...... foreach (Sy ...

  3. Java连接Oracle database小结

    利用jdbc连接Oracle数据库,首先得选对jdbc的版本. jdk6需要ojdbc6.jar这个包.如果选错的话,调用isValid()这个方法时,有可能有这个异常: Exception in t ...

  4. javascript马赛克遮罩图片切换效果:XMosaic.js(转)

    新鲜出炉的javascript图片切换特效,实现的是马赛克遮罩切换.在flash里,好实现遮罩动画很简单,不过JS实现起来就有些困难了. XMosaic.js,与XScroll.js和XScroll2 ...

  5. ubuntu12.04 安装CS:APP Y86模拟器

    下的第一UBUNTU12.04下Y86模拟器的安装:(參考http://archive.cnblogs.com/a/1865627/ 作适当改动) 1.安装bison和flex词法分析工具 sudo ...

  6. 由12306动态验证码想到的ASP.NET实现动态GIF验证码(附源码)

    背景: 12306网站推出“彩色动态验证码机制”,新版验证码不但经常出现字符叠压,还不停抖动,不少人大呼“看不清”,称“那个验证码,是毕加索的抽象画么!”铁总客服则表示:为了能正常购票只能这样.而多家 ...

  7. Jquery揭秘系列:Validation实现

    之前讲了一部分揭秘系列的东西,由于年初的时候在改项目,也没有写下去.现在开始闲下来了,会继续写没写完的东西,各种原生js实现Jquery的功能. 转入正题,说一下今天要讲的东西. 相信很多tx在项目里 ...

  8. Sqlite创建增删改查

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  9. 教你一招:Win10切换输入法与Win7一样(Ctrl + 空格)

    对于win10的朋友,大部分人对输入法都不习惯,如果你把英语输入法删除了,在中文输入法里没有美式键盘,让ctrl+空格与ctrl+Shift都能在搜狗输入法和美式键盘切换.下面小编就教你怎么让Win1 ...

  10. 关于IE8兼容svg图片问题解决

    参考 http://www.zhangxinxu.com/wordpress/2013/09/svg-fallbacks/ 对博主表示感谢,详述请参考上述链接 直接把代码复制过来了,备份记录下 < ...