How we do MVC – View models


A while back, I went over a few of the patterns and opinions we’ve gravitated towards on our current large-ish ASP.NET MVC project, or, how we do MVC.  Many of these opinions were forged the hard way, by doing the wrong thing many times until we found the “right” opinion.  Of course, many of these opinions are only really valid in the constraints of our project.  While the domain of this project isn’t important, here are some key aspects to consider:

  • AJAX is used very, very sparingly.  Section 508 compliance is required
  • XHTML compliance is also required
  • XHTML DTD validation is also required
  • All (well, 99%) operations revolve a single uber-entity.  Think customer relationship management, where everything you do deals with exactly one customer
  • Snippets of information repeated across many screens
  • Screens are either edit, or view, but never both.  (99% never)

Given these constraints, these opinions may or may not apply to the project you work on.  Again, patterns are all about tradeoffs, benefits and liabilities.  But, opinionated software is like building a bullet train.  It goes extremely fast, but only in the direction you build it.

That said, I’m going to go over some of the main aspects of our MVC usage in a series of posts – starting with ViewModels.

ViewModel design

For our application, the ViewModel is a central aspect of our MVC architecture.  One of the first dilemmas facing MVC developers is to decide what the “M” in MVC means in ASP.NET MVC.  In Rails, this is fairly clear, the M is ActiveRecord (by default).  But in ASP.NET MVC, the “M” is silent!  Its out-of-the-box architecture offers no guidelines nor advice on what the M should be.  Should it be an entity?  Data access object?  DTO?  Something else?

Sidenote – the term DTO is far overused.  DTO is a Data Transfer Object.  The pattern describes the usage, not the shape of a type.  Just because an object is all properties and no methods does NOT mean it’s a DTO.

For us, the ViewModel is inextricably linked to Views, which leads us to our first rule:

Rule #1 – All Views are strongly-typed

I think I’ve gone over this one enough, as I really can’t stand magic strings and loose contracts.  A dictionary as ViewModel is a very loose contract between the Controller and View.  While on rare occasions we still need to pass information in the dictionary part, we’ve limited this to supporting information to help render some of the lower-level pieces of the View, and are used for some “plumbing” pieces, these pieces do not show up in our Controller action nor are they visible when you design the view.

Rule #2 – For each ViewModel type, there is defined exactly one strongly typed View

We’ll get into how we do this soon, but this rule has a lot of implications:

  • ViewModel types are distinct from our Domain Model types
  • The choice of what View to show can be decided strictly on the shape of your ViewModel
  • Re-used pieces in a View (through Partials) can be decided through re-using ViewModel types

image

On the first point, we never pass an Domain Model entity straight into the view.  Most of the time, we only show a slice of information from a single entity.  And many other times, the same snippet is shown in many places.

Rule #3 – The View dictates the design of the ViewModel.  Only what is required to render a View is passed in with the ViewModel.

If a Customer object has fifty properties, but one component only shows their name, then we create a custom ViewModel type with only those two properties.  Because we only have one ViewModel type per View, we shape our ViewModel around only what is displayed (or used) in that View.  Why is this a Good Thing?

For one, just having a ViewModel points us to the right View.  We need any other information other than your ViewModel type to pick the correct View.  This also means we no longer need to concern ourselves with locating views by some arbitrary name, as the ViewModel is the View.  Things like RenderPartial, which I have to select a name, become rather pointless at that point.

Rule #4 – The ViewModel contains only data and behavior related to the View

For the most part, our ViewModel contains only data.  Most, if not all aggregations/calculations or shaping is done through our Domain Model.  But occasionally, we have View-specific behavior or information, and that rightly belongs on our ViewModel.

We’ve looked at how we design our ViewModel and what it looks like, but how does it get there?  If we create all these distinct ViewModel types separate from our Domain Model, didn’t we just create a bunch of work for ourselves?  We thought so too, which is why we developed AutoMapper on this project.

Building the ViewModel

When we introduced AutoMapper into our MVC pipeline, we had a real problem.  Do Controllers need to do the mapping between Domain Model and ViewModel in each action method?  That becomes rather annoying for unit testing, as the mapping operation could warp things to a state that it becomes difficult to pull things back out.  For example, or EditModels (ViewModels for forms) are very string-y, where DateTimes, Ints, Decimals etc are represented as strings.  This comes from us using Castle Validators (future post, I promise) for validation. 

So more moving parts, a dependency across all controllers?  No, mapping in our Controller action just won’t do.  Instead, we’ll use an Action Filter to do the work for us:

image

A request comes in, handled by an Action.  The Action does its thing, ultimately deciding how to respond to the request.  In many cases, this means rendering a view (ViewResult).  From there, our Action Filter comes into play.  On our Action method, we decorate it with an AutoMap attribute to configure the source/destination type pair to be mapped:

[AutoMap(typeof(Product), typeof(ShowProduct))]
public ActionResult Details(int id)
{
    var product = _productRepository.GetById(id);

    return View(product);
}

Very trivial, yes, but here we see that we still use the strongly-typed version of the View method, so that means that our model on the Action side, which I call the Presentation Model (feel free to pick a better name), is the strongly-typed ViewModel for the moment.  The Presentation Model, which the Action creates, can be an entity, an aggregate root, or some other custom aggregate component that we build up.

From there, we decorated our action with a filter that specified we need to map from Product to ShowProduct.  Why do we have to specify the source type?  Well, many ORMs, including NHibernate, rely on proxy types for things like lazy loading.  Instead of relying on the runtime type, we’ll explicitly specify our source type directly.  This also helps us later in testing, as we can whip through all of our controller actions using reflection, and test to make sure the source/destination type specified is actually configured.

The filter attribute is very simple:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class AutoMapAttribute : ActionFilterAttribute
{
    private readonly Type _sourceType;
    private readonly Type _destType;

    public AutoMapAttribute(Type sourceType, Type destType)
    {
        _sourceType = sourceType;
        _destType = destType;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var filter = new AutoMapFilter(SourceType, DestType);

        filter.OnActionExecuted(filterContext);
    }

    public Type SourceType
    {
        get { return _sourceType; }
    }

    public Type DestType
    {
        get { return _destType; }
    }
}

We simply capture the types and delegate to the real action filter for the work.  This is again because I believe in separating metadata in attributes from the behavior they perform.  Attributes just don’t work well for behavior.  Instead, I’ll create a separate action filter:

public class AutoMapFilter : BaseActionFilter
{
    private readonly Type _sourceType;
    private readonly Type _destType;

    public AutoMapFilter(Type sourceType, Type destType)
    {
        _sourceType = sourceType;
        _destType = destType;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var model = filterContext.Controller.ViewData.Model;

        object viewModel = Mapper.Map(model, _sourceType, _destType);

        filterContext.Controller.ViewData.Model = viewModel;
    }
}

The BaseActionFilter is just a class that implements the various filter methods as virtual members, so I can override just the ones I need to use.  The AutoMapFilter pulls the original PresentationModel out of ViewData, performs the mapping operation, and puts the mapped ViewModel into the ViewData.Model property.  From there, the strongly-typed view for our specified ViewModel type is rendered.

Because AutoMapper can flatten source types, we often find our ViewModel to simply follow a property chain for various pieces of information.  Again, we let our View shape that piece.  If we decide not to flatten, it’s usually because we’re creating a partial that re-uses a ViewModel type across other parent ViewModel types.

###

Wrapping it up

Designing ViewModels is quite ambiguous with MVC, as the shipped platform doesn’t offer any guidance or opinions in that area.  But by forming rules around our ViewModel, we can create a path and direction for our innovation.  Our rules are designed to strengthen the relationship between the View and the Model, with a concept of ViewModel – a Model designed exclusively for exactly one View.

In the next post, we’ll look at designing our views – for both viewing and editing data, and how we’ve crafted opinionated HTML builders to eliminate a lot of duplication and enforce standardization.

Expressions Cheat Sheet