Three Steps to Get Fat-Razor MVC Views on Diet

ASP.NET MVC Views give you the power of Razor View Engine with the flexibility of writing C# code. However, if you do too much C# you will end up with Fat-Razors that need to go on a diet or you might be risking separation of concerns attacks

Skip table of contents

Introduction

Model and Controller drawn as small circles beside a View several times their size. (enlarge)

Let me first define the terms that I will be referring to:

Presentation Code: The minimal code in a view that is necessary to display visual elements, without making any business decisions. This is a fictional simple example:

// Business Logic Code. This code understands how the business works
// (don't do this)
if (Model.IsMonthly && !Model.IsPaid && Model.Payment >= 12 && ...)
{
  <p>Some warning message</p>
}

// Presentation Code. This code is only concerned with whether to
// show or hide a UI element.
if (Model.ShouldWarn)
{
  <p>Some warning message</p>
}

Fat-Razor: I’ve coined this term to define ASP.NET Razor views with a lot of Razor/C# code that is not presentation code. This is about the Razor/C# code specifically and not about HTML/CSS/JS.

Model: This is an overloaded term. However, in this context, it is an object of data representation such as a record in the database.

Viewmodel: A data transfer object that is view-specific and carries exactly what the view needs, no more and no less. Generally, viewmodels are non-reusable and each one is tightly coupled to a single view, though there are exceptions depending on what you are doing.

To give you a flavor of a model and a viewmodel:

// An example of a model
public class Person
{
    public Guid PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public short YearOfBirth { get; set; }
    public DateTime LastAuth { get; set; }
    // This might represent a one-to-many db relationship
    public Order[] Orders { get; set; }
}

// An example of a viewmodel
public class ProfileViewModel
{
    // Doesn't necessarily need an id

    // Combined first and last name
    public string Name { get; set; }
    // Calculated from YearOfBirth
    public short Age { get; set; }
    // E.g. "One day ago" and constructed from LastAuth
    public string LastSeen { get; set; }
    // Calculated by projections on Orders
    public short NumberOfAllOrders { get; set; }
    public short NumberOfUndeliveredOrders { get; set; }
}

Why would it hurt to have Fat-Razor views?

Before we discuss a solution, we need to agree that there is a problem.

Distribution (non-centralization) of Business Logic

Fat-Razors carry similar criticism to what the old classical ASP had (and what PHP currently has), that is, no separation of concerns. The concern of this piece of code is to show visuals and not to do business logic.

The business logic for your domain should be centralized. Fat-Razor views push business logic into your views, which gives more control to what should be a dumb layer and violates the “power in the wrong hands” principle. You would be risking a fragile business logic that will break more often.

Due to the scope of this post, I will just tell you “It is bad, don’t do it” and won’t dive into the topic. Don’t take my word for it, though: here is Uncle Bob’s Clean Architecture (new tab).

Unit Testing

You’ve heard it before, views aren’t unit testable (well, you can unit test them with some tools like Razor Generator (new tab) but this is not the norm), or maybe more accurately shouldn’t be unit tested and should rely on a different type of testing such as web testing like Visual Studio Testing Tools, Selenium or Capybara. So, Fat-Razor views mean more code you cannot unit test.

Refactoring

How many times did you change your code and it compiled fine until you hit the page with your browser? And boom, your refactoring did not propagate to the views, because the tool sees them as text. Today, it is not that dramatic with tools like ReSharper, but it is still a problem. Having less C# code would reduce the problem size but not eliminate it.

Why would views become Fat-Razor views?

These are the symptoms I have seen in the industry; feel free to add more reasons in the comments.

Using a generic model rather than a specific viewmodel

The MVC beginner tutorials try to show how simple MVC is by loading a record from the DB, transferring it into an object with an O/R mapper (Entity Framework, NHibernate and the like) and then passing it to the view which renders it. Building a blog with MVC tutorials, I am looking at you.

Good example for beginners, gets them up to speed, but only works in trivial applications. Why? If your view is not getting exactly what it wants, then it is going to have to do some inspections on the model, hence, code and more code! If you treat the view as dumb and give it exactly what it wants, you will reduce the need for it to have any non-presentation logic.

Get your Fat-Razor into shape

A WWF promotional photo of the wrestler Razor Ramon, arms folded. (enlarge)

Big Boss Man, RIP, was my early childhood wrestling hero (he was strong and cool!), but Razor Ramon fits better in this post.

Now that I have touched on some of the necessary concepts around this subject, I could highlight some steps for Thin-Razor views.

1 – Use a viewmodel for your view

Do not take a model from the database and throw it directly into your view. This will couple your views to your entities and will probably invite business logic code.

My favorite way of doing this is to use a CQRS type query; this article does a good job of introducing CQRS: Introduction to CQRS (new tab). I query exactly what my view needs into a viewmodel, with no intermediate model (or DTO). Have a look at Dapper – a simple object mapper for .Net (new tab).

If you have a different architecture or legacy code, perhaps you could have a layer that maps a model into a viewmodel. Have a look at AutoMapper (new tab); it may help.

2 – Try to differentiate business from presentation code

When writing views, always ask the question “Is this presentation code or business logic?” It is a hard question, as in some cases the line between the two is fine. Sometimes I ask for a second opinion and encourage a healthy debate.

3 – Package repeated presentation patterns into HTML Helpers and Partial Views

Sometimes one repeats the same presentation code again and again in different views. I do that as well, following the agile principle “Do the simplest thing that works, then refactor”; when I notice a pattern forming, I refactor it into an HTML Helper or a Partial View.

Conclusion

As they say, “Less is more.” The less code you write, the less you have to maintain — and the better that code is, the safer you are if the developer inheriting the project knows where you live.

I reviewed this post several times to make it shorter; however, it is a big topic and still not complete. If you have any points that you would like to share, then please do comment.