English 中文(简体)
CQRS - The query side
原标题:

A lot of the blogsphere articles related to CQRS (command query repsonsibility) seperation seem to imply that all screens/viewmodels are flat. e.g. Name, Age, Location Of Birth etc.. and thus the suggestion that implementation wise we stick them into fast read source etc.. single table per view mySQL etc.. and pull them out with something like primitive SqlDataReader, kick that nasty nhibernate ORM etc..

However, whilst I agree that domain models dont mapped well to most screens, many of the screens that I work with are more dimensional, and Im sure this is pretty common in LOB apps.

So my question is how are people handling screen where by for example it displays a summary of customer details and then a list of their orders with a [more detail] link etc....

I thought about keeping with the straight forward SQL query to the Query Database breaking off the outer join so can build a suitable ViewModel to View but it seems like overkill?

Alternatively (this is starting to feel yuck) in CustomerSummaryView table have a text/big (whatever the type is in your DB) column called Orders, and the columns for the Order summary screen grid are seperated by , and rows by |. Even with XML datatype it still feeel dirty.

Any thoughts on an optimal practice?

问题回答

Yeah, there s a confusion that arises. Here s how it happens: First, in order to really help new people understand what CQRS is all about, and to really drive home how it differs from the typical layered architecture, people say things like, "Your view models can be completely flat", and "You should have one table per view model in your Query database."

But that is really just meant to drive the point home ... it is not true that you must have only one table per view model (though in most cases you probably should). What those statements are trying to say is this: "You ve got to shake yourself free of some rules that you ve followed for so long about normalization. With the CQRS architecture, you have the opportunity to make database tables in your Query channel that are completely shaped according to the needs of your view and nothing else. Make sure you take full advantage of that. Don t go halfway by normalizing these tables just because it is what you re accustomed do. Instead, go ahead and do things that used to be considered unthinkable, such as making one database table per view model, or making your view model tables completely flat."

There are still cases such as yours where the schema that would best serve your needs would have multiple tables. You might even (God forbid) do a Join or two. That s fine, as long as you really have designed the database tables to serve your view, and not vice versa. But be careful, it is easy to slip down the slope of normalization and sharing data between many views in the Query database. Don t go there ... there s simply no reason and it incurs more cost than benefit. The main goal is this: Your View logic should be dead, dead simple. You want the intelligent rules living on the Command side of the house, and a little bit in the Subscribers that populate the data in the Query channel. So the code that reads from the Query database and shows data on a screen should be dead simple (almost as simple as a "passive view").

Update: As further backing for the statement that it is not "forbidden" to do some joins as long as you have designed the database shape to best serve the task you re achieving, consider OLAP. The star schema is a perfect example of a database schema designed to support reads, that fits perfectly in the Query side of CQRS, and that does involve joins. The joins are kept simple, and they are in place to further enhance the goals of the Read tasks performed against the database.

if anyone is actually saying that your viewmodels should be flat, they are either oversimplifying their example or they are talking a bunch of nonsense. hierarchical data is not bad and should not be avoided in your viewmodels.

there really are no best practices for this, though. it s all very subjective in how you load up the data. you need to find a solution that works well for your current team and system. and you should also understand what other options are out there because you ll probably run into a situation where your current solution is inadequate.

here are some of the ways I handle this, depending on the application i m working on, in C# / .NET:

  • Datasets and straight ADO.NET, and bind the dataset directly to the screen s controls ** write straight SQL code to load the dataset ** use views in the database to load the dataset ** use stored procs to load the dataset

  • NHibernate and DTO / Viewmodel objects ** i typically use views when going down this route - I ll create a suite of views on top of my domain s schema, that denormalize the data into the model i need, and then use NH to load it up via a second set of maps

  • DTO / Automapper from domain model ** i don t like this approach unless I know that I already have everything from my domain model loaded in memory. i ll use a tool like Automapper to transfer data from my domain model into a DTO / ViewModel

i m sure there are other options, but these are the three that i use most often, in order of how often i use them. they all have their own cost / benefits. but the important thing to understand is that you can and should retrieve the data in a manner that makes it easy for you to populate your screens.

I think people are missing the point of CQS (or CQRS, same thing really). CQR is simply a pattern to say that you should have separate models for read and writing. What those model are is completely up to your implementation requirements.

Folks that been talking about this recently usually describe extremely simple architectures and implementation to drive home that the fact that most enterprise software written today is over-designed and over-engineered.

One of the benefits of a CQRS ES approach is that you can devise really simple (read fast) view data. As a result of the event stream you can shape your read side data in any way you want. Hence lots of people like to de-normalise the data so that it is optimised for reading. You don t of course have to. But why wouldn t you? Think about the frequency of reads in a typical LOB compared to the writes.

Just in case you find it helpful and would like to see some code samples I have written a more detailed answer on my blog. You can find the post here: http://danielwhittaker.me/2014/10/05/build-master-details-view-using-cqrs-event-sourcing/

If you want to work with different dimensions in your views, there are no problems to do that. There is noting saying you cant use multiple view models below a view. The denormalizer is responsible for populating the database views with correct data. Have a look at this post, it explains how the denormalizer works and may put you in the right direction concerning your question.





相关问题
nHibernate one-to-many inserts but doesnt update

Instead of getting into code, I have a simple question. Default behavior for a simple one-to-many is that it inserts the child record then updates the foreign key column with the parent key. Has ...

How Do I copy an existing nhibernate object as a new object?

I a persisted NHibernate object that I would like to repersist as a new entity. How do I get NHibernate to save this object as if it was a new? I am thinking I might create a session interceptor to ...

join across databases with nhibernate

I am trying to join two tables that reside in two different databases. Every time, I try to join I get the following error: An association from the table xxx refers to an unmapped class. If the ...

WPF - MVVM - NHibernate Validation

Im facing a bit of an issue when trying to validate a decimal property on domain object which is bound to a textbox on the view through the viewmodel. I am using NHibernate to decorate my property on ...

NHibernate Search in a List using ICriteria

I have my class X : public class ClassX { public virtual IList<ClassY> ListY { get; set; } ... } My ClassX mapping (using Fluent) ... HasMany<ClassX>(x => x.ListY ) ....

热门标签