Northwind Starter Kit ReviewThat 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.
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.
More posts in "Northwind Starter Kit Review" series:
- (26 Jan 2012) Conclusion
- (24 Jan 2012) That CQRS thing
- (23 Jan 2012) It is all about the services
- (20 Jan 2012) From start to finishing–tracing a request
- (18 Jan 2012) If you won’t respect the database, there will be pain
- (16 Jan 2012) Refactoring to an actual read model
- (13 Jan 2012) Data Access review thoughts
- (12 Jan 2012) The parents have eaten sour grapes, and the children’s teeth are set on edge
- (11 Jan 2012) Data Access and the essence of needless work, Part II
- (10 Jan 2012) Data Access and the essence of needless work, Part I
Comments
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.
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.
Two interfaces, two object models, two places to change. Increasing complexity without real profit. CQRS is not neccesary in most cases.
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.
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?
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.
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"?
@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.
Whole point of CQRS is to change data in other aggregate and query data across aggregates.
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.
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!!
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.
@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.
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.
Nic, Take a look at this post: http://ayende.com/blog/4503/component-oriented-design-and-why-it-be-retired
@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).
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.
@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.
@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/the_futility_of_emergence/
@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.
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?
Karep, Baby steps :-) You can't refactor an architecture in a single bound
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.
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.
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
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.
"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.
@flukus Doesn't that really depend on how you design the flow in your UI?
Comment preview