Udi Dahan talks on CQRS in Paris
By Jérémie Chassaing on Tuesday, November 17, 2009, 16:37 - Domain Driven Design - Permalink
Udi Dahan gave a very good talk yesterday evening at Zenika, there was only few attendees… perhaps because it was on a Monday evening. Whatever, there was barely not enough place already in the Italian restaurant where we moved after.
I won’t make a full report, just talk about some interesting points.
First of all, the session focused mainly on why you should do CQRS and not how. Second point, the talk was not about event sourcing, but you already now that you can do CQRS without event sourcing.
Something we should accept : Stale Data
The paradigm of usual architecture’s best practice has a serious flow : when you show data to your users, it’s already stale.
Is it important ? Yes.
Is it a problem ? Not really.
The world have worked with stale data for years, and it was handled rather gracefully until now. Computers have reduced the time span, but when the data appear on the screen, it’s stale.
Tel it to your users, they will accept it. Find with them what is acceptable. 1 second, 10 seconds, 1 minute, 1 hour, 1 day ? The users are used to it in there own business. Do it too.
Queries
What’s the purpose of queries ? To show data. Not objects.
So why should the data from the database come across 5 layers through 3 model transformations ? It’s a bit overkill to display data.
Why not just this : The UI read data from the database and displays it ?
No DTOs, no ORM, not business rules executed on each query.
You simply define a Persistent ViewModel (thank’s Udi, I like this description of the Q side), and display it directly to screen. It should be as simple as one database table per UI view.
Of course you need a way to keep the Persistent ViewModel up to date, but we’ll see that later.
Commands
On the other side, there are commands.
It should be done in 3 phases :
Validation
Is the input potentially good ? Structured correctly, no missing field, everything fit in ranges ?
This can be done without knowing current state, and be done outside of entities command handling.
Rules
Should we do this ?
Here, the decision is taken using current state.
It leads to a discussion about UI design. In order to handle the user command as well as you can, you have to capture the user intent in the command.
In CRUD applications, the new data is sent by the UI layer. You have to extract the user intent from that data to know if you can process the data.
There is a huge difference between UserMovesToNewAddress and CorrectTheMisspellingInUserAddress from a business point, but in a CRUD application you would probably end with the same Update data…
State change
What’s the new state ?
It’s the easy part once the rules are applied.
Domain Model
What aren’t they for ?
Validation : commands are validated before the model is called. Do not bloat your domain model with this.
Queries : entity relationships for reading are unnecessary. You can do eager loading on your Aggregate Roots safely, they’ll never be used for queries that need only partial information.
What are they for ?
Answer to the question : should we do what this valid command is asking ?
If the answer is yes, change the state !
Maintain the query model up to date
There are two main ways to maintain query model up to date.
You can use something like views or ETL to transform data from the domain data to the shape required by the query side.
If you prefer, or when your domain persistence is not compatible with this option (OODB, Event Storage..), you can publish events from you command side, and provides handler’s on the query side that will maintain the views state in the relational database (or a cube… or whatever). A denormalization will happen here.
What do we gain from this ?
Asynchronous model
The model is deeply asynchronous, it’s not a matter of tweaking things with threads. It’s asynchronous from the ground up, at domain level.
Your user sends a command, and your design is good if you can answer : “thank you, we will come back to you soon…”. Take the time needed to fulfill your user wish, he will be happy !
Scalability
By relaxing the rules, the system becomes more scalable.
Domain persistence choice
The domain is accessed only to process rules and state changes. There is no need to join tables, filter rows. So you can easily use an non relational database.
Possible options are a OODB or an Event Storage (for event sourcing).
You can still use a RDBMS with or without an ORM if you’re more familiar with these technologies.
But the persistence mechanism becomes an implementation detail from from Command side that will not interfere with your queries.
Conclusion
Ooops, I said it was not a complete report… but it actually is. Every point was interesting ?
After the talk we had a discussion about forecasting and other interesting subjects. Perhaps more on this later.
There was a video camera in the room, so I think the guys from Zenika will try to put it on the internet when they have time. I’ll add the link when available.
If you was here and have a picture of the event, I would be glad to put it in the blog :D
Comments
Excellent post! It's always great to soak up Udi's wisdom. A few days ago I did a write-up of the various "thin mapping layers" (as Greg calls them) that could be used to talk to the database:
http://jonathan-oliver.blogspot.com...
The one big question I have after reading your post is that, when applied to MVC, wouldn't you still want to use a strongly typed DTO "model"--even when the model matches a database query? I'm trying to reconcile this against the "one model in, one model out"/one model per controller/strongly typed model stuff.
After re-reading, I think I've got it. Basically "one model in, one model out/strongly typed model stuff still works. The difference is we can skip the *additional and now unnecessary* ViewModel DTO layer that translates the "database row" into a ViewModel object. Instead, we just pass the ActiveRecord object representing the denormalized database row directly to the view. Does that sound about right?
I'm wondering if the "EditModel" still applies--where a model is created from the data POSTed during a form submit. Then the commands that are pushed to the bus are built from the values in the EditModel object.
@jonathan> Yes you got it right.
Since your datatables are a private concern of your view
you can skip a lot of decoupling layers.
Of course I would still use automapping between
DataRecord and a view DTO. It will act as a very
very thin layer to avoid Magic Strings when a
column is used several times.
On the controler side, if you use a bus, commands dtos
will be available from the domain assemblies.
So you do validation and send it on the bus.
Hi,
Thanks Jeremie, I arrived late at the presentation, but it was really interesting. I just missed the query part...
I found this architecture really interesting, but I am yet wondering about some points...I started a series on my blog (not yet published) to expose my understanding of a "classical DDD-like" approach...then I plan to write about why and how to switch (or not) to CQRS to expose my wonderings (I need more maturation of the subject...).
In fact, the most embarassing subject for me is the denormalized Query database. You say that typically it is "one table per screen (I extend or part of a screen if you have different parts)" and Udi says that relations are not interesting for Query...but I am a little embarassed with this, seee the following example.
Example : you have a very simple search screen of People having differents Roles and you would like to display in the results grid one line by People with theirs Roles in a column (and not one line per Role => really awful UI to my opinion) ? It is really simple to bind an Employee entity with a list of Role to make this kind of screen, but if you just rely on direct binding from Database you end up with :
1) a SELECT N+1 problem if you choose to keep it in 2 tables
or
2) awful treatment in UI to denormalize if you choose to keep only one table
or
3) perhaps (I hope it is the preferred one, is it the same thing as Jonathan says?), you use some Persistent ViewModel (I see it as a DTO, no?) that reflects this relation that has to be binded to screen
Thanks again for this brief and the discussion after the presentation ;).
Clément
@clement> If you just want to show a coma separated list of roles on each line... you can do this during denormalization.
If you want a table with variable column count, you cannot map directly one table to one view.
Of course use the simplest thing that works. There are several options :
anyway, the one view->one table recommandation is not an absolute rule because sometimes we need to display things that are not just tables. But choose the most appropriate viewmodel and don't be affraid to denormalize as much as you can.
@Clement,
The big thing about denormalization is that you want to try to avoid JOIN statements in your database queries. That being said, this doesn't mean you can't issue multiple database queries, either separately or together. For example, NHibernate has the ability to "batch" queries and bring back multiple result sets in a single DB round trip. The biggest overhead when fetching from the database is lots of JOINs and round-trips. If you have no joins and only one or two round trips, you've eliminated a lot of that overhead.
Even so, you'll want to try to get everything into "screen-based" DTOs so that it's all in a single object and you can just send that DTO to the UI/view, like Jeremie said.
Hi,
I'm not sure I understand the difference between the different level of validation and rules application. May I take an example of a classic customer form with a VAT number field. The objective is to mark the customer tax exempt if it has a Vat number inside EU but ouside of my country.
At the UI, I think I have a clear idea of what kind of rules to guarantee. Length, type, basic validations like zip code, phone number. So, I supposed that if I need to validate the VAT Number, it should be done here even if it requires calling a web service.
At the user register command handler, I pass as input my customer info and then I'm lost.
Do I decide to tax exempt my customer at this layer or should I just ask the domain layer ? Do I have a customer constructor with tax exempt argument, or will it be calculated in the constructor itself.
In fact, what does a valid command really means and what is left to the domain. I see lot of theory around CQRS but finding reference code or app is hard.
Sébastien
@sebastien> The validation depends only on command data, the rules depends on command data *and* domain current state.
The validations answer to the question : Does the command have a valid "syntax".
The rules answer to the question : Should we realy do what the command asks ?
Hi Guys
If anybody is stil following comments here, can someone elaborate a bit on the answers for Clément Bouillier?
Say I assign a user a role, resulting in a UserAssignedRole event containing the UserId and RoleId. When i go to update my "UsersWithRoleName" read-side table, am i suppose to have the roles stored seperately on the read side, in order to find the role name, to put in?
This could potentialy mean storing the entire model as well as the denormalized tables on the read side.. ..would you guys recommend this or do you have an alternate approache?
Kind regards
Olav
@Olav> To show the data on the read side. So you'll have all that data in the database. This is where you'll find names and descriptions.
One difference with your domain model is that it will be stored with presentation in mind instead of transactions.
The other difference is that you can rebuild it from your event log.
Hope it helps
Jérémie > Thanks for your reply.
I'm sorry for beeing thick. I get that data should be stored for presentation. That's what UsersWithRoleName is for. But when a "UserCreated" event arrives, i need to get the associated role name from somewhere, in order to fill in all the fields in that table.
So besides the "UsersWithRoleName" table you would have a "Role" table on the read side, to get the role names from, when new users arrive?
@Olav> yes, it should.
Sweet - thanks :)