views:

289

answers:

3

At the moment, i got quite badly fashioned view model.

Classes looks like this=>

 public class AccountActionsForm
    {
        public Reader Reader { get; set; }
        //something...
    }

Problem is that Reader type comes from domain model (violation of SRP).

Basically, i'm looking for design tips (i.e. is it a good idea to split view model to inputs/outputs?) how to make my view model friction-less and developer friendly (i.e. - mapping should work automatically using controller base class)?

I'm aware of AutoMapper framework and i'm likely going to use it.

So, once more - what are common gotchas when trying to create proper view model? How to structure it? How mapping is done when there's a multiple domain object input necessary?


I'm confused about cases when view needs data from more than 1 aggregate root. I'm creating app which has entities like Library, Reader, BibliographicRecord etc.

In my case - at domain level, it makes no sense to group all those 3 types into LibraryReaderThatHasOrderedSomeBooks or whatnot, but view that should display list about ordered books for specific reader in specific library needs them all.

So - it seems fine to create view OrderedBooksList with OrderedBooksListModel view model underneath that holds LibraryOutput, ReaderOutput and BibliographicRecordOutput view models. Or even better - OrderedBooksListModel view model, that leverages flattening technique and has props like ReaderFirstName, LibraryName etc.

But that leads to mapping problems because there are more than one input.
It's not 1:1 relation anymore where i kick in one aggregate root only.
Does that mean my domain model is kind a wrong?

And what about view model fields that live purely on UI layer (i.e. enum that indicates checked tab)?

Is this what everyone does in such a cases?

 FooBarViewData fbvd = new FooBarViewData();
   fbvd.Foo = new Foo(){ A = "aaa"};
   fbvd.Bar = new Bar(){ B = "bbb"};
   return View(fbvd);

I'm not willing to do this=>

var fbvd = new FooBarViewData();
   fbvd.FooOutput =  _mapper.Map<Foo,FooOutput>(new Foo(){ A = "aaa"});
   fbvd.BarOutput = _mapper.Map<Bar,BarOutput>(new Bar(){ B = "bbb"});
   return View(fbvd);

Seems like a lot of writing. :)


Reading this at the moment. And this.


Ok. I thought about this issue a lot and yeah - adding another abstraction layer seems like a solution =>

alt text

So - in my mind this already works, now it's time for some toying.

ty Jimmy

+2  A: 

Here's one item that dawned on us after we had been struggling with alternatives for a long time: rendering data is different from receiving data.

We use ViewModels to render data, but it quickly turned out that when it came to receiving data through forms posting and similar, we couldn't really make our ViewModels fit the concept of ModelBinding. The main reason is that the round-trip to the browser often involves loss of data.

As an example, even though we use ViewModels, they are based on data from real Domain Objects, but they may not expose all data from a Domain Object. This means that we may not be able to immediately reconstruct an underlying Domain Object from the data posted by the browser.

Instead, we need to use mappers and repositories to retrieve full Domain Objects from the posted data.

Before we realized this, we struggled much with trying to implement custom ModelBinders that could reconstruct a full Domain Object or ViewModel from the posted data, but now we have separate PostModels that model how we receive data.

We use abstract mappers and services to map a PostModel to a Domain Object - and then perhaps back to a ViewModel, if necessary.

Mark Seemann
This is a nice tip but unluckily (or luckily) i've figured out this part. Model binding by Id/mapping works like a charm. Another thing - it's good to separate view model into view model you render and view model you receive when form is posted.
Arnis L.
I'm interested how to handle cases when view needs more than one entity view model and they need to be grouped. Any tips about that?
Arnis L.
I'm not sure I understand that additional question, but you can compose ViewModels from other ViewModels. However, you're a smart guy, so you probably know that already, so I guess you meant something else...
Mark Seemann
Thanks for the compliment. I'll try to add some explanation in my question. :)
Arnis L.
@Mark Seemann i remember Jimmy wrote somewhere that they do NOT bind view model back to domain model. They perform changes 'manually'. And when you think about it - it sounds correct if we follow DDD. Binding back is one of the things that drags towards anemic domain model and fat service layer.
Arnis L.
+2  A: 

While it may not make sense to group unrelated Entities (or rather their Repositories) into a Domain Object or Service, it may make a lot of sense to group them in the Presentation layer.

Just as we build custom ViewModels that represents Domain data in a way particularly suited to a specific application, we also use custom Presentation layer services that combine things as needed. These services are a lot more ad-hoc because they only exist to support a given view.

Often, we will hide this service behind an interface so that the concrete implementation is free to use whichever unrelated injected Domain objects it needs to compose the desired result.

Mark Seemann
This makes sense to me too. I'm just wondering about actual implementation from technical point of view. I'm not willing to create countless constructors just to aggregate entities and produce a bit more complex view model. Can automapper give some aid at this?
Arnis L.
I haven't been able to reduce our stuff to something that isn't complex in some way. It's much simpler with rich clients because you don't have all the round-tripping and loss of data. I don't see AutoMapper helping a lot with this, because its force is in convention-base mapping. Maybe you should ask this specific question as a new SO question, and then I promise not to answer :)
Mark Seemann
One idea came to mind => action could be decorated with `MapTo(typeof(OrderedBooksListModel))` and call view with anon type `View("OrderedBooksList", new{library,reader,bibliographicrecord[]})` then - through filter, using some reflection magic and automapper map it properly. Would that be an acceptable solution? Any drawbacks?
Arnis L.
That could possibly work. Drawbacks? Yes: anonymous types used as output are essentially just magig string dictionaries, so you might as well just return a `Dictionary<string, object>`. The loss of type-safety can potentially bite you as you refactory, but whether that's an acceptible price to pay is a judgment call.
Mark Seemann
+2  A: 

It's tough to define all these, but here goes. We like to separate out what we call what the View sees from what the Controller builds. The View sees a flattened, brain-dead DTO-like object. We call this a View Model.

On the Controller side, we build up a rich graph of what's needed to build the View Model. This could be just a single aggregate root, or it could be a composition of several aggregate roots. All of these together combine into what we call the Presentation Model. Sometimes the Presentation Model is just our Persistence (Domain) Model, but sometimes it's a new object altogether. However, what we've found in practice is that if we need to build a composite Presentation Model, it tends to become a magnet for related behavior.

In your example, I'd create a ViewFooBarModel, and a ViewFooBarViewModel (or ViewFooBarModelDto). I can then talk about ViewFooBarModel in my controller, and then rely on mapping to flatten out what I need from this intermediate model with AutoMapper.

Jimmy Bogard
Arnis L.