Ayende @ Rahien

Refunds available at head office

Northwind Starter Kit Review: That CQRS thing

This is a review of the Northwind Starter Kit project, this review revision 94815 from Dec 18 2011.

It is obvious from reading the code that there was some attention given to CQRS. Unfortunately, I can’t really figure out what for.

To start with, both the Read Model and the Domain Model are actually sitting on the same physical location. If you are doing that, there is a 95% chance that you don’t need CQRS. If you have that, you are going to waste a lot of time and effort and are very unlikely to get anything from it.

In the case of NSK, here is the domain model vs. the read model for the customer.

imageimage

I marked the difference.

I am sorry, there is nothing that justify a different model here. Just needless complexity.

Remember, our job is to make things simpler, not make it hard to work with the application.

Comments

ashic
01/24/2012 10:25 AM by
ashic

Having read models and domain models in the same location or different locations doesn't really determine the applicability of CQRS - if needed you could easily move the read model to a separate store. The simplest implementation of CQRS would be where you have two interfaces to the same database and same data where changes are done through a thicker interface and queries done through a thinner one (possibly ISession). You've been advocating this approach for some time now. CQRS doesn't necessarily mean having separate data stored for reading or that they should (or should not) be in the same location.

Rafal
01/24/2012 10:59 AM by
Rafal

Isn't CQRS definition so broad that almost everyone is doing it without even knowing? After all you update the data with 'update table' statement and query it with 'select * from' - this is CQRS at the lowest level possible.

gandjustas
01/24/2012 11:00 AM by
gandjustas

Two interfaces, two object models, two places to change. Increasing complexity without real profit. CQRS is not neccesary in most cases.

tobi
01/24/2012 11:13 AM by
tobi

Also, the aggregate root concept is kind of broken. I have never seen a domain model which could be split into aggregate roots in a meaningful way. Data is interconnected.

Will S.
01/24/2012 11:18 AM by
Will S.

What is the reason behind these series of posts? I know you like to challenge developers and PMs but this crosses the lines. Are we the "Rails" community or ".NET" community?

Will S.
01/24/2012 11:20 AM by
Will S.

What is the point of these series of posts? I know you like to challenge developers and PMs but this crosses the line (among other things). Are we not the ".NET" community or the "Rails" community?

This is very unprofessional.

James McKay
01/24/2012 11:22 AM by
James McKay

Is it just me, or does anyone else think that CQRS is a case of "when all you have is a hammer, everything looks like a nail"?

ashic
01/24/2012 11:35 AM by
ashic

@tobi: Aggregates are ways of managing what data needs to be atomically consistent and what can be eventually consistent. If you think all data in a large system can always be instantly consistent, you would be wrong.

@james: So you would have your queries go through the same "layers of abstraction" that deals with changes? Why go through them when all you need is to query a data store? Perhaps your understanding of CQRS is constrained to a certain implementation and you're taking a hammer-nail approach in your viewpoint.

gandjustas
01/24/2012 12:05 PM by
gandjustas

Whole point of CQRS is to change data in other aggregate and query data across aggregates.

Nic Wise
01/24/2012 12:51 PM by
Nic Wise

Ayende (or anyone else)

whats your thoughts on making the domain model the read model (ie, the right hand side one), and using either extension methods, or a helper class (CustomerHelper??) to do the rest of the work.

public bool CanBeDeleted(this Customer cust)

etc

Good? Bad?

I tend to do the latter, tho I HATE the naming, but I've not come up with anything better so far.

Yves Reynhout
01/24/2012 12:53 PM by
Yves Reynhout

I don't get why people use layers and tiers and object oriented languages in the first place. I mean, I can probably do faster what ya'll are doing right in my database using T-SQL/PL-SQL.Procedural Code is da BOMB!!

Wayne M
01/24/2012 01:01 PM by
Wayne M

I don't even quite understand the CQRS thing in general; I get the idea that you want (I think, correct me if I'm wrong) a separate model (?) that just does SELECTs (i.e. the query) and a different one entirely that does all of the creating/updating (the command)? I have yet to find a good demonstration/tutorial on that but that really smells like needless complexity no matter how you slice it; what are you accomplishing by having two classes do the same thing (interact with the database)? I suppose it might work if you have a transactional database for doing things and a separate database (a data mart, if you will) for reading the data, but realistically places of business have one single database.

James McKay
01/24/2012 01:01 PM by
James McKay

@ashic: Not at all. If CQRS is the best solution to a problem, then I'm all for it. It's just that it's not the best solution to every problem, and I sometimes wonder if it gets misapplied by people who are eager to jump on the bandwagon and attempt to "prove themselves" in this area -- or people who think it fits their nebulous idea of what a "best practice" is and therefore believe that This Is How You Should Architect Your System.

Martin Fowler's article at http://martinfowler.com/bliki/CQRS.html has some pretty good advice about when CQRS is useful and when it isn't. Having separate models for your commands and queries in particular does introduce a violation of DRY, and that needs to be balanced against the benefits that you receive from it in other areas (e.g. performance).

@Yves: That approach only works for simple projects with simple logic. Once you get into modelling the complexities and politics of everyday life, it's not so straightforward.

Yves Reynhout
01/24/2012 01:08 PM by
Yves Reynhout

I've been doing this for years in rather complex projects ... it's my ticket to overcharging and job security. Plus, I didn't have to learn a new thing in like 20 years. I think it's stupid to chase new technology all the time.

Ayende Rahien
01/24/2012 01:16 PM by
Ayende Rahien

Nic, Take a look at this post: http://ayende.com/blog/4503/component-oriented-design-and-why-it-be-retired

ashic
01/24/2012 02:34 PM by
ashic

@james: You seem to be seeing CQRS as separate models and that need not always be the case. Having a model for changes and using your ORM to query (with ISession for example) is a form of CQRS. You seem to suggest querying should be done directly using an ORM rather than through "layers" that enforce rules; yet you dismiss CQRS. Separate query interface (ISession) does not necessarily mean a separate "model" (whatever that is).

Mads Stevenson
01/24/2012 02:43 PM by
Mads Stevenson

Hi,

To separate the read side of the write side (independently of be using the same physical DB), is not good?!

This kind of separation sounds good for me, because you can design queries based on UI, with specific DTOs, while I can design commands to affect my domain model, via write side of it, based on user tasks.

In read side, all I need is a way of to execute performatic queries that returns the result-set as soon as possible (like SqlDataReader). In the write side, I use ORM (yes, your ORM :P), to persist and load entities from the database.

James McKay
01/24/2012 03:25 PM by
James McKay

@ashic: Not at all. I'm not advocating going from one extreme to the other. The correct response to misuse is not disuse, but proper use.

Christopher Wright
01/24/2012 04:31 PM by
Christopher Wright

@ashic: if CQRS covers standard usage of an ORM, what doesn't it cover? If it covers every sort of database access, then it's a useless term.

Related: http://lesswrong.com/lw/iv/thefutilityof_emergence/

ashic
01/24/2012 05:38 PM by
ashic

@christopher What part of Command Query Responsibility Segregation says an ORM can't be used? I would refer you to a post by Greg from 2010: http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/

CQRS does not prescribe any particular data storage - it merely states that the channel used for changing things should not be the channel used for querying things. If you associate more to it and it makes things more complex, don't blame for doing so.

Karep
01/24/2012 07:16 PM by
Karep

Ayende: From the link you provided to Nic

I would much rather see behavior moved into the entities (upgrading them from persistent DTOs to actual entities), querying being centralized in a repository ...

So you liked repositories a year ago or do I misunderstand something?

Ayende Rahien
01/24/2012 08:30 PM by
Ayende Rahien

Karep, Baby steps :-) You can't refactor an architecture in a single bound

João Bragança
01/24/2012 08:41 PM by
João Bragança

CQRS is just a pattern. Methods that return void update state. Methods that don't say something about current state.

@Karep

You are missing the point. ISession IS a repository. If you want abstraction it gives you IQueryable. The point of these blogs is that wrapping up ISession behind 3 layers of abstractions just to move around DTOs is a waste of resources.

flukus
01/24/2012 09:27 PM by
flukus

I over use the CQRS pattern because it drives the UI in a good direction.

CQRS UIs ARE command based instead if crud based. Crud based systems get very confusing for users and developers once they start getting complicated.

Brett
01/25/2012 03:05 AM by
Brett

I'm not convinced they are using CQRS in this sample just because of the separate read model (e.g. no command methods on aggregates), but I also don't think that's the problem either. The problem appears to be a misuse of aggregates, particularly due to code smells like public setters and the aggregates for the most part are anaemic. In this case I agree though, separate read models seem superfluous... but they do allow the read side to evolve independently so they may end up looking quite different.

@Yves: LOL

Stan
01/25/2012 05:49 PM by
Stan

Reasons to separate Read Model and the Domain Model when them sitting on the same physical location: 1) Fetch DTOs directly in queries instead of selecting domain entities and then convert them to DTOs. I prefer to map DTOs to db and not to write converters; 2) Queries can use ISession directly and controlls fetching strategies when commands may continue to use repositories because in most cases in commands you access specific aggregate by its id.

Christophe
01/25/2012 11:29 PM by
Christophe

"There is a 95% chance you don't need CQRS" Just curious, how,did you come up with that 95% figure? This sounds so precise that it must be backed by a tremendous experience of this kind of architecture. Or you get an access to some serious statistics about our industry that even our industry doesn't know about.

Kim
02/20/2012 04:46 PM by
Kim

@flukus Doesn't that really depend on how you design the flow in your UI?

Comments have been closed on this topic.