36

I'm having a debate with my coworkers in the design of the read side of a CQRS application.

Option 1: The application read side of my CQRS application returns DTOs, e.g:

public interface IOrderReadService
{
    public OrderDto Load(int id);
}

public class SomeController
{
    public ActionResult SomeAction(int id)
    {
        var dto = ObjectFactory.GetInstance<IOrderReadService>().Load(id);
        var viewModel = Mapper.Map<OrderDto, SomeViewModel>();
        return View(viewModel);
    }
}

public class SomeOtherController
{
    public ActionResult SomeOtherAction(int id)
    {
        var dto = ObjectFactory.GetInstance<IOrderReadService>().Load(id);
        var viewModel = Mapper.Map<OrderDto, SomeOtherViewModel>();
        return View(viewModel);
    }
}

Option 2: The application read side returns ViewModels, e.g.:

public interface IOrderReadService
{
    public SomeViewModel LoadSomething(int id);
    public SomeOtherViewModel LoadSomethingElse(int id);
}

public class SomeController
{
    public ActionResult SomeAction(int id)
    {
        return View(ObjectFactory.GetInstance<IOrderReadService>().LoadSomething(id));
    }
}

public class SomeOtherController
{
    public ActionResult SomeOtherAction(int id)
    {
        return View(ObjectFactory.GetInstance<IOrderReadService>().LoadSomethingElse(id));
    }
}

From the research my coworkers and I have done on the matter, responses appear mixed - it looks like it really depends on context. So I ask you, my dear StackOverflowians:

Does one approach seem to have clear advantages over the other? If so, what are they?

Marcos Dimitrio
  • 6,651
  • 5
  • 38
  • 62
Josh Kodroff
  • 27,301
  • 27
  • 95
  • 148
  • 1
    I should add that the major point of disagreement is in edit screens that are dealing with children of aggregate roots. – Josh Kodroff Oct 12 '11 at 23:27
  • Shameless reference to myself: Your question just made me think of the consequences of returning ViewModels. Thanks for the food for though of the day! http://stackoverflow.com/q/21408226/253098 – SystematicFrank Jan 28 '14 at 14:35

4 Answers4

32

The general advice is one projection per screen (Greg Young) or even one projection per widget (if I understand Udi Dahan correctly).

To consolidate read models into DTOs that once again have to be mapped into separate views contradicts the whole purpose of an optimized read model. It adds complexity and mapping steps that we tried to get rid of in the first place.

My advice: Try to get as close as possible to SELECT * FROM ViewSpecificTable [WHERE ...] or something similar if using NoSQL in your thin read layer.

The concept of Aggregate Roots and their "children" doesn't have too much of an implication on the read side, since these are domain model concepts. You don't want to have a domain model on the read side, it only exists on the write side.

UI platform-specific transformation as mentioned by Mohamed Abed should be done in the UI layer, not in the read model itself.

Long story short: I'd opt for option 2. To not confuse it with a platform-specific ViewModel, I'd rather call it ReadModel but have one per view anyway.

Dennis Traub
  • 50,557
  • 7
  • 93
  • 108
  • The projections are tough to do because we only have a single, relational data store that's being done with EF code first. I wouldn't know how to make views on top of that. – Josh Kodroff Oct 13 '11 at 13:15
  • Do I understand you correctly: You want to extract your DTOs/ViewModels from the EF-Model which you're using on your write side as well? Sounds like there's just an artificial separation between read and write model through mapping of data from a single model. In this case there's no big difference between using DTOs and ViewModels, since you're losing the inherent benefits of read/write separation anyway. – Dennis Traub Oct 13 '11 at 13:31
  • [Addendum:] In CQRS you wouldn't want to make your views on top of the EF-Model (or at least that would be only the first step when transforming legacy code). You would want to build your views directly from the database. No matter if it's NoSQL, separate denormalized tables or just views over your 3NF data. – Dennis Traub Oct 13 '11 at 13:36
  • I'd like to avoid eventual consistency if I can (thus a single data store). I don't want to have to worry about a customer adding an item to an order and it possibly not being there when the order refreshes. – Josh Kodroff Oct 14 '11 at 16:52
  • If you build your views on top of your tables in the RDBMS there is no eventual consistency. But if you build your views on top of your ORM-based model, it's not very different from good old layered architecture. – Dennis Traub Oct 14 '11 at 18:05
  • To be more specific, I'm talking about views as in database views written in SQL. If you want to only use your 3NF datastore, getting your read model through database views is perfectly fine (and as consistent as a Relational database can get) – Dennis Traub Oct 14 '11 at 18:18
  • So using ASP.NET MVC as our presentation technology example, the read service would return an XDto with all the data necessary for the screen (and only the necessary data). Then would the controller map that to an XViewModel (which would have nullable fields formatted as strings appropriate for display and IEnumerables of SelectListItems) to be passed off to the view? – Josh Kodroff Oct 17 '11 at 21:47
  • Basically that is the idea, yes. Glad I could help. – Dennis Traub Oct 18 '11 at 09:21
  • What if there are multiple different views for same readmodel? For example `ProductOffer(product_code, name, price, attribute_a, attribute_b,... )`, but two views, `ProductOfferListItem(product_code, name, price)` and `ProductOfferDetails(product_code, name, price, attribute_a, attribute_b,... )`,.. The issue comes for filtering, which can't operate only on `ProductOfferListItem` as it doesn't have enough information, but it must return only ProductOfferListItem,.. or, what if query's predicate operates on multiple internal attributes, which must not be sent to frontend? – kravemir Oct 18 '19 at 07:06
  • I've opened another question for that issue: https://stackoverflow.com/questions/58446154/in-ddd-cqrs-should-readmodel-act-as-viewmodel-if-where-belongs-responsibility – kravemir Oct 18 '19 at 07:44
14

One of the primary principles of DDD/CQRS is that you shouldn't be editing the view model. Instead, task-based screens should guide the user towards issuing explicit commands. If you can't come up with task-based screens, you should be using a different form of CQRS like the one I describe here:

http://udidahan.com/2011/10/02/why-you-should-be-using-cqrs-almost-everywhere/

Andrea Scarcella
  • 3,233
  • 2
  • 22
  • 26
Udi Dahan
  • 11,932
  • 1
  • 27
  • 35
  • We are using task-based screens. The DTOs are only returned by the read side in order to be mapped to ViewModels. – Josh Kodroff Oct 13 '11 at 13:16
  • Then on the [HttpPost] controller action the ViewModel is mapped to a command, which is then issued to the write side. – Josh Kodroff Oct 13 '11 at 13:33
6

I would prefer to return DTO to separate the application layer from presentation technology (because each presentation technology may have some requirements on the structure of presentation model) for example a web MVC application binding is different than WPF MVVM binding, also you may require some properties/fields in view models that has nothing to do with application data like for example (SliderWidth, or IsEmailFieldEnabled, ...). Also for example if using WPF MVVM i would need to implement INotifyPropertyChanged interface to allow binding it is not convenient nor related to implement this interface in the application read service.

so i would prefer separating the concern of read data representation from the actual presentation technology and view model.

So option 1 is better for me

Mohamed Abed
  • 5,025
  • 22
  • 31
  • 2
    In CQRS one is not supposed to use the view model to change data. Instead explicit commands should be sent. – Udi Dahan Oct 13 '11 at 12:40
  • 2
    Yes, I already was talking about the read model only, maybe i mis-described the example (slider), but what i meant is like (progressbar) or whatever read-only property to be bound to read only view – Mohamed Abed Oct 13 '11 at 12:57
3

A read model is a projection of the write model. It's there to fulfill a specific purpose. In your case that seems providing view models to your MVC controllers. As such, why go thru the trouble of mapping DTO's to Viewmodels? The only reason I could think of was that the benefit of having two separate read models might not outweigh their maintenance cost. But do consider that "merging" read models for the purpose of "reuse" and "lowering maintenance costs" increases complexity for the developer (Can I change this table? Hmmm, I now have two (or more) consumers I have to take into account - smells a bit like database integration all over again).

Just my thoughts.

Yves Reynhout
  • 2,982
  • 17
  • 23