Subscribe to my blog

CQRS is too complicated

Published on 2011-9-28

Is something I hear all too often at conferences and on Twitter, and more often or not it is said because of either a basic misunderstanding of what CQRS is or is not - or perhaps because they've dipped their toes into the hyperactive DDDCQRS mailing list and been scared away by all the white coat discussion that goes on in there a lot of the time.

The other day, the sentiment was yet again voiced by somebody of whose opinion I respect on Twitter and I ended up in about five minutes writing a gist explaining why I didn't think this was the case (Writing 4000 word essays is an hour's work if I'm feeling ranty), I've tidied it up a bit and decided to throw it below as it works well in a blog entry.

A basic summary

At the highest level CQRS just means maintaining a happy division between the reads and writes across your system - that is, having the reads in your system executed in a thin clean manner appropriate to the views you want to retrieve (one model), and your writes going through all the crazy logic you need such as validation, updating queues, third party systems, processing business rules (another model)

Consider the traditional and very-tongue-in-cheek N-Tier architecture I have created here in powerpoint, seen in a million "architecture" presentations in ASP.NET webforms shops across the world:

 

Now modify it a little bit so that our reads haven't got to go through all that cruft, haven't got to somehow amene themselves to a bunch of "DAL objects" that are created with the very best intention of standardising our access to some form of database (and normally optimised for the write actions anyway).

We can instantly make our lives a lot easier by creating a pile of code optimised for creating views for our presentation layer, perhaps doing a bit of raw SQL or calling a sproc to generate the view for us. We can helpfully formalise this arrangement and for the most part set down a rule that the direction of travel down those two paths is one way (towards the DB for writes and away from the DB for reads).  Funnily enough - most systems that do that BOL/BLL/DAL/OCKS stuff end up with something that looks like this anyway because it's too hard to do everything through a single model.

This is now a form of CQRS - at the highest level we've effectively split our system into two models and done something that's very similar to what we'd call CQS if we were doing it at the method level.  This in itself should surely be enough to convince you  that CQRS itself is not complicated and it might be a useful thing to look further into.

Of course, as you go further down the rabbit hole...

Some examples

CQRS can be achieved by using a document database like Raven or Couch - using your documents as a write store, using your indexes as a query store.

 

It can be achieved with your favourite ORM (Even better if you can actually use that O and that M and get some good old OO going) - if you want to use your objects for encapsulating business logic and go directly to the the queries to project the data you need for views (HQL, SQL directly, SPROCS, whatever) - from the same database even, providing this remains efficient enough for your needs. (Funnily enough, "our" collective weak attempts at creating domain models with NHibernate are what led to us re-discovering the need for two models in the first place in my opinion).

 

Of course you may well end up with two databases anyway, as trying to query a database comprised of tables that represent state in your "objects" can be pretty inefficient, with the read store updated from the write store using hooks in your write system to generate pre-calculated views or data that's more applicable to generating views - this is not a bad model and can work too, it's still CQRS.

 

CQRS gets the "complicated" label because people often associate it directly with event sourcing, which requires that little bit more of up-front development in order to get the level of elegance you won't find in the above scenarios. However, even event sourcing is really simple once you look at it - and is a natural progression from some of the other ways of "doing" CQRS - which can be a bit muddy (not that there is anything wrong with systems that are a bit muddy). Note that I'm not mentioning DDD here At All - which is where a lot of heavy learning lies, and nearly none of us do anyway.

Consider hooking those events in your system to manually flatten/re-arrange data into other stores as outlined above? Does that work for that one other store? How about a reporting store? How about full text search? What about integration with third party systems and the data they want to see from you? How about the boardroom reports your CEO now wants on his desk each morning before he starts his day?

Youch. Deciding your single source of truth is the already written state gives you an amount of inflexibility, which you may or may not be happy with up to a point.

Updating other views of this truth after small changes can be inefficient and awkward. Recovering after introducing any write bugs to the system can be expensive also. Hell - even changing your model can also be expensive as database migrations are hardly the easiest things if you're trying to work with multiple stores and layers all over the place. When your powerpoint presentations start looking like this you have complexity issues- and these complexity issues aren't caused by CQRS, they're caused by having complex powerpont presentations.

Moving to events and jumping through a few hoops to make this possible can open up a world of simplicity, and if it's not for you there are other options open to you. CQRS is not complicated - trying to shoehorn the responsibilities of read and write through a single model is complicated. Most of us realise that going through a standard "BLL, DAL, BOL, TLA, CRA, P) layer for both reads/writes is dumb, and CQRS is a good way of formalising this decision.

Another tdlr;

You can see that clearly there is a natural progression from the very basics to having the need to go for a full blown event sourcing system with publishers/subscribers/servers/eventual consistency once the complexity of trying to manage a more "simple" solution starts to overwhelm.

Unless you have that complexity and that need then of course trying to thrust an ivory tower designed architecture onto a system that doesn't need it is going to seem complicated. Hint: If your technical solution is more complicated than your original problem you're probably doing it wrong.

blog comments powered by Disqus

Olle


Very nice written Rob. You've managed to simplified the idea perfectly. Read it twice, now I'll push it to my co workers.

Kristof Claes


Thanks for the explanation Rob.There's one little thing that isn't really clear to me. The queries to generate view models, do you put them right where you need them? I'm hearing some people (like ayende) say "Just put the read code in your controller" but that seems to conflict with the SRP and the concept of thin controllers.What would be the typical way of doing something in an ASP.NET MVC application?

Paul Cowan


I think the main problem with DDD and CQRS is that they are misappropriately used.The vast majority of systems do not need this level of sophistication. What is more worrying is that .NET has adopted CQRS as their new and shinny. I shudder at people using either for CRUD but I suspect it does happen.These are specific situations for big distributed scenarios and not something to be shoe horned into most apps.The rule for full blown DDD and CQRS should be:If you don't have more than one bounded context then don't f'ing use it.What I am working on right now uses messaging and I have a domain model mapped to NHibernate with acompanying view models but I am by no means to CQRS or full blown DDD because what I am working on does not warrant the level of complexity of DDD or CQRS and would only add months onto completion.

robashton


Paul :: I couldn't agree more about the new and shiny and your problem with people deciding they're "going to do DDD or going to do CQRS" without either understanding what those things are or why they might want them.However, I'm not sure that you actually read the article, because I'm sure that codeofrob.com/images/internal_codeofrob_com/2.png is too far away from the most basic thing you can do (even if you're just doing CRUD), and I'd classify that as CQRS (although Udi would disagree)

Paul Cowan


@rob I read the article but I disagree that the architecture image is CQRS. CQRS as explained by Udi and Greg young is about messaging and eventing across bounded contexts with long running sagas.The architecture in the image could all exist in the same box. You do have a point though, maybe this architecture is way too complicated for CRUD. You could also say maybe datasets are ideal for CRUD but I'm not sure I would go that far but you should question the logic behind every decision.I use messaging in my current scenario because the services are used by more than one product or website. I feel I have the right justification for using messaging. If this was one website or product using messaging then I still could argue that I have some long running tasks that outlive a basic web response and basic messaging might be justified as a way of facilitating long running tasks.As I said earlier, I am by no means doing CQRS or DDD.

robashton


Okay Paul, I see where you are coming from - but disagree that CQRS is about messaging and eventing across bounding contexts although I know this is a point of discussion if you read the the e-mails between Greg/Udi here: groups.google.com/.../rasTHHmnzpoJMessaging and eventing across bounding contexts is a form of responsibility segregation certainly, but aren't necessarily the C and the Q being talked about here.HOWEVER, if we're going to get in a pickle over definitions rather than just doing the Right Thing and Building Software which is what your overriding desire seems to be anyway then we're in trouble anyway - I agree with everything you say apart from the definitions so um - that's pretty much all I can say :-)

robashton


Kristof :: it's a rich man's problem that isn't really all that important, there is no "do it this way" answer. All that matters is that your code is readable, maintainable enough by other developers and testable/tested enough to meet the quality requirements set by the customer. This is a bullshit answer but it's kinda the way it is.In my ideal world the controllers wouldn't really exist and I'd have a pipeline that would end up with a command being handled or a query being returned, I guess ala Fubu/OR.

Quooston


I'm tired of propeller heads and their shiny toys. It's about DELIVERY FFS; leave the shiny toys at the door. The reality is that the very "best" developers are only enjoying themselves when they're playing with the toys, so this is what you get... complexity a-la hoopla for no bloody apparent reason. Drives me absolutely insane. I will see your "oh so clever" shite and raise you half the dev time and a happy customer any day. 99% of the time you don't need any of this, just be absolutely pragmatic and focus on delivery. If you can't deliver because you need to evolve, then evolve... pragmatically. And continue like so until you're out the door.D E L I V E R Y.

Simon Timms


I have to agree with Rob's assessment of messaging being largely unnecessary, at least when we define messaging as an out of process activity. It is perfectly possible, even desirable, to implement CQRS as a single process and even set it up to be synchronous. Just as there are maturity models for REST I see there being maturity models for CQRS with the lowest level being synchronous processing of requests. As we move up the maturity model we add things like message queues, message distribution and subscription technologies like NServicebus, snapshotting and event storage. One certainly needs to pick and choose where to apply CQRS as not everything is a nail. However I find myself at odds with some people's argument that CQRS should only be used in collaborative domains as I think there are other advantages which make it applicable in other situations. For instance having a dedicated read model allows for some nifty data-warehouse updating tricks.

robashton


Qoosten - did you even read the blog entry?

John[no] Nolan


Finally had time to digest this. The 'heavyweight' comments I made on twitter were exactly all that eventing, messaging over BCs. The reason why the normal BOL /BLL/DAL/DAO/ OCKS seems less complex is because it is a nice linear power point slide (which is less scary for everyone). AS soon as you get into a dual set of models, it is adding complexity (which is more scary).What I think you describe is just a good design. When I've read about CQRS (with DDD) it is normally Enterprise grade and I've never seen the point in using it.

Alex Hoffman


I also think it's too complex. Part of the issue in my opinion is that CQRS suffers from a "high pattern dependency" smell - http://bit.ly/riHX4v

David


Thanks, I think this is a helpful way of looking at an inconsistently defined term.I don't really get the "shiny toy" responses, or the blanket "too complex" responses, and I wonder if they're written in response to the title rather than the article.CQRS can get complex but the first level described here essentially involves using sql or some intentionally simple mechanism to drive some of your views. What exactly is shiny or complicated about that?

Dennis Doomen


Are you suggesting that Udi's excellent depiction of how a fullblown CQRS/ES architecture should look like is crap?

robashton


No - where do you get that impression?

Quooston


@robashton I'm all for CQRS and event sourcing, but I think you need justification before applying that layer of inherent complexity. I don't like people knee-jerking to every pattern they've read just because it exists instead of focusing on providing value to the paying customer... which is what I was trying to say.Came across a bit strong perhaps... it had been a tough day after dealing with a pattern obsessed, self proclaimed "legend"...

robashton


And I'm asking where you think the inherent complexity is in any of the examples (in the context in they're presented) - most of my entry talks the lack of complexity, and how each progression towards having more technological complexity seeks to address the accidental complexity that occurs in the level below once you reach a certain threshold.I either think you didn't read my blog entry and just decided to rant because of the title, or you've misunderstood everything in it - the latter I can fix if you tell me how exactly ;-)

Alexander


That is super information! Thanks for sharing! I’m going to Tweet about your blog.

Tim Van Wassenhove


I think CQRS is all about simplification instead of sophistication..It is a lot easier to apply divide and conquer on a code base. One part is responsible for the reads (Q) and another part is responsible for the writes (C).In 'traditional' architectures you end up with one model that has to cater both read AND write, which usually leads to a model that is at least suboptimal for one of both tasks (in my experience, usually the read side).In the spirit of the GNU text utilities, I prefer a lot of tools that each do one thing (and one thing only) very well, than one big tool that tried to do all things..

Mike McG


This is a much-needed article that should help to combat the latest round of cargo cult programming in .NET. Dahan/Young have cultivated a particular, highly-complex definition of CQRS (DDD + event sourcing + messaging) which the .NET masses have met with either unbridled adoration and awe, or with complete rejection, neither of which is appropriate. The conclusion of this article (as I read it) is very subtle to them. To emphasize, * CQRS itself does not directly require DDD patterns, event sourcing, asynchronous messaging, message buses, eventual consistency, multiple data stores, field-based notifications, compensating events, SQL+ORM, NoSQL, etc etc etc; * relative to a single domain model, in it's simplest form CQRS is *no more complex*.These nuances are difficult to accept for those who believe they already understand (i.e. have been indoctrinated). This is clearly demonstrated by several commenters of your carefully presented article, who seem to directly refute that CQRS can be as simple as you've described. This is bothersome, as is the general historical tendency for .NET developers to exhibit a cult-like lack of critical analysis, *both in terms of SOLID and design fundamentals, as well as in more holistic terms such as maintainability, business value, learning curve, etc.*To very briefly speculate, the economic ecosystem of the .NET stack invites a different developer community than that of, say, Java or Haskell. Deploying .NET requires significant fiscal outlay, so the primary audience is business (whose needs are typically shorter-termed). Many other stacks do not have this barrier, and therefore permit a larger proportion of academics within the community. *Crucially, this leads to a more skeptical overall developer culture,* in which formulations of CQRS like Dahan's would be more thoroughly scrutinized and appropriately contextualized. Instead, the lower degree of skepticism in the .NET community permits such voices of confidence and novelty to be accepted widely, regardless of their accuracy.Well-written and rational article; thank you.

Psyllos


I have some difficulty to imagine how to deal with some information in a CQRS architecture. Typically, if I want store information about a Portable Contact. There is a lot of description into this type of document. What are commands that are involved and especially their parameters? In other words, should I create as many commands as properties of this contact? How about the creation command?I can not understand the granularity of commands to create, and I can found a more complex example in the CQRS resources.

Ryan


CQRS solves the problem of data over time. Because you're implicitly logging all mutations/actions across your domain, you always have a full audit trail and the ability to restore the system to an earlier state.For some domains that alone is reason enough to apply the pattern.