Partager via


Controller Action Design in MVC

Validate, Act, Translate, and Respond. That’s about it.

I’ve been trying to come up with a nice acronym for how to structure code in Actions for some time now, and this is the best I have managed. I wish it spelt a nice word – so if you’ve a better suggestion shout up. What I’m trying to say is –

“don’t stuff all your business logic and all sorts of other code into your controller”

and,

“follow a common, clear pattern to writing your controller actions”

This is similar to the AAA acronym for unit testing; Arrange, Act, and Assert; which really helps developers focus when writing tests.

Therefore, my slightly awkward VATR acronym suggests that in an MVC action you;

1. Validate; check the input, either through automatic model binding, explicit validation checks, or a combination of both.

2. Act; apply the business logic – save, fetch data, etc.

3. Translate; convert entities returned from the business logic into View Model entities. See this post if you want to understand the motivation for this further.

4. Respond; render a view, or redirect to another action, etc.

This does not include “inspect database” or “loop through all posted data and….” steps, because I believe they should be encapsulated in the business logic, and therefore not in your controller. It also doesn’t include authorisation, error handling, or other concerns that can be handled with filters etc. Let’s take some example code and see what I mean.

Example 1: The Simple Case

Taking a typical Index action, perhaps for a blog site;

public ActionResult Index()

{

    var articles = _logic.GetLatestArticles();

    var model = new HomeIndexViewModel

    {

        Articles = Mapper.Map(articles),

        Today = DateTime.Today,

        Username = UserInfo.Username

    };

    return View(model);

}

In this example, there is no data input directly to the action, so Validate is notable by it’s absence. Anything the action uses should be validated of course, such as the Username retrieved from a helper class; but that validation is encapsulated elsewhere. Next, we Act - by calling the GetLatestArticles method in my business logic. This returns a list of article business entities.

I don’t want to pass these business entities to the view (why?), so we Translate them into a View Model using a helpful Mapper class. And finally, we Respond with a command to render a view using our View Model.

Example 2: A POST Action

Upping the complexity slightly, let’s consider a POST that needs to save some data;

[HttpPost]

public ActionResult Display(HomeDisplayViewModel model)

{

    if (ModelState.IsValid)

    {

        _logic.UpdateArticle(model.Article);

        return RedirectToAction("Index");

    }

    else

        return View(model);

}

Here we have the following flow;

1. Validate – we rely on automatic model binding, and check the ModelState.IsValid flag

2. Act – if validation is successful, we save the article using a business logic call

3. Translate – there is no need for translation in this example

4. Respond – we either pass back a View or a Redirect depending upon the validation outcome.

Summary

I think keeping this simple approach in mind when writing actions helps to keep your code well factored and easy to test and maintain. I’ve not shown what happens when things get more complex (for example, if you need to check the result from your business logic) but the pattern still holds.

There is one case when I would include additional logic – pretty much just checking whether I should respond with a Partial View (when in an Ajax request) or a full View (when not Ajax). I see this as part of the Respond logic though, so I don’t think it breaks the pattern. In many cases I extract this to a filter anyway.

There may be other scenarios too – do you have any? Or a better acronym? Feel free to chip in…

Comments

  • Anonymous
    August 17, 2010
    Confirm ~ Act ~ Transform ~ Send (CATS) Confirm the input is valid. Act on the input. Transform the result. Send the result to the client. This was a great article, thanks.

  • Anonymous
    August 18, 2010
    Interesting suggestion - how about VATS? I prefer Validate to Confirm, and it still sounds like it spells something :-) Simon