Encapsulation is the enemy of the user interface
I got this question a while ago from Kyle, and I think is is a great one. It is especially great since it is an exchange of emails that resulted in the following (all of which are Kyle words):
I've been annoyed lately by the MVVM pattern. It seems like it requires that the data on your business classes be public so that the view-model can get at it, and that completely breaks encapsulation and goes against standard OO design theory (in my opinion).
The UI layer should be allowed to reference the data layer. I recalled a post you wrote where your UI needs to basically pull things out of queries and such directly (that's what I understood it to mean, anyway). I'm not sure how to pull this off easily just yet, because it seems like it would still break encapsulation somewhere down the line, but it's an interesting thought.
And yeah, I realized after sending the email about CQS. I've decided that my preferred way is actually having my model be able to create a view-model. It's still not pretty, but it's much better (in my view) than having all public data on my business models. I can use commands to bind directly to the model, and the view-model can cause that to happen correctly.
I thought about CQS more, and have a really nice way of doing the whole shebang, I think. It does kind of use your "Two different models for read vs write" concept. I've even come up with a little pseudo-enterprisey application to write using this design style. You'll like it - it's a Netflix for books [[netflix for books is a library]], essentially.
My answer to that is that Kyle is correct. On the one hand, we have the needs of the UI to show information, and on the other hand, we want to have good encapsulation for our business entities. UI forces us to expose information to the user, and that encourages properties laden models. The problem with this approach is that often we try to make use of the same model for several tasks, such as using business entities for user interface, or even asking the business entities to generate the view models that they represent.
CQS is a design methodology that is aimed at resolving this conflict, at its heart, it is actually very simple. It simply stipulate that you are going to have two different models for representing it. One for reads (queries) and another for writes (commands). Once we accept that, we can see that we can evolve each of those models independently. And then we get to the point where we see that the data storage mechanism that we use for each model can be optimize independently for each use case.
For example, when using commands, we generally perform lookups by primary key alone, so we can avoid the overhead of indexes, or even select a storage format that is suitable for key based lookups (DHT, for example) while updating the query data store as a background process which allow the entire system to stay stable under high degree of stress.
In other words, once we have split the responsibilities of the system up so we don’t overload the responsibilities of a single model to be both read and write capable, we are in a much better position to shape the way we handle our software.
Comments
IMO don't mix up CQS with CQRS. they are two vastly different creatures.
CQRS is Greg Youngs idea with a command model and a query model where you can have queries for a certain gui.
CQS intends to make it possible to make pre/post/invariant checks in DBC without changing state and thus breaking already evaluated conditions.
There may be commands and queries in both cases, but the problems and contexts are not the same.
Agreed. Artificial separation of the 'model' and 'view model' leads to duplication of logic and lots of boilerplate code to generate data transport objects. It's much better to allow the use of ORM-generated DAL in views + some smart ambient session management.
Who are you agreeing with, Rafal? :)
Yes, development is much more productive after understanding CQ(R)S. I love mixing them together like an alcoholic drink.
Depending on what you're writing, avoiding DTOs can be wrought with adding a whole level of complexity integrated in the code that matters. Data-binding is the rage. Restricting actions available to users is easy; restricting views available to users is easy; but restricting specific details of data transferred to those common views... That's where things can go pear-shaped. I hate the thought of introducing code into views that essentially says "oh, I just won't show that."
I'm not sure I'd go so far as to introduce a separate models for view vs. update. I do like to define DTOs specific not only to views, but to things like user roles. IMO a view needs to remain ignorant of the business entities, but I guess that's why I tend to think more MVP/MVC rather than MVVM.
Yes, darned users, always want to interfere with my beautiful coding with silly UIs, Command Lines and gasp API calls.
Another advantage of splitting models is that you then don't need to map from your domain model to your view model (and vice versa). Why hydrate domain objects in the first place if you can create simpler objects in the first place?
Comment preview