New book: Programming Microsoft ASP.NET MVC, Second Edition

662841cvr.inddUpdated for ASP.NET MVC 3, Dino Esposito’s Programming Microsoft ASP.NET MVC, Second Edition , is now available. You can see the book’s “Contents at a Glance” and read its Introduction in this earlier post. In this post we share an excerpt from one of the book’s chapters, Chapter 7, “Design Considerations for ASP.NET MVC Controllers.” Here’s the book part the chapter comes from:

PART II ASP.NET MVC Software Design

CHAPTER 5 Aspects of ASP.NET MVC Applications. . . . . . . . . .189
CHAPTER 6 Securing Your Application . . . . . . . . . . . . . . . . . . . .227
CHAPTER 7 Design Considerations for ASP.NET MVC Controllers . . . .253
CHAPTER 8 Customizing ASP.NET MVC Controllers . . . . . . . . .281
CHAPTER 9 Testing and Testability in ASP.NET MVC . . . . . . . .327

 

 

CHAPTER 7

Design Considerations for ASP.NET MVC Controllers 

Part of the inhumanity of the computer is that, once it is competently programmed
and working smoothly, it is completely honest.
—Isaac Asimov

The controller is the central element of any operation you perform in ASP.NET MVC. The controller
is responsible for getting posted data, executing the related action and then preparing and
requesting the view. More often than not, these apparently simple steps originate a lot of code.
Worse yet, similar code ends up being used in similar methods, and similar helper classes sprout up
from nowhere.

ASP.NET MVC comes with the promise that it makes it easier for you to write cleaner and more
testable code. For sure, ASP.NET MVC is based on some infrastructure that makes this possible and
easier than in Web Forms. A lot, however, is left to you—the developer—and to your programming
discipline and design vision.

Architecturally speaking, the controller is just the same as the code-behind class in Web Forms. It
is part of the presentation layer, and in some way it exists to forward requests to the back end of the
application. Without development discipline, the controller can easily grow as messy and inextricable
as an old-fashioned code-behind class. So it isn’t just choosing ASP.NET MVC that determines whether
you’re safe with regard to code cleanliness and quality.

In this chapter, we’ll explore an approach to ASP.NET MVC design that simplifies the steps you
need to mechanize the implementation of the controller classes. The idea is to make the controller
an extremely lean and mean class that delegates responsibility rather than orchestrating tasks. This
design has an impact on other layers of the application and also on some portions of the ASP.NET
MVC infrastructure.

Shaping Up Your Controller

Microsoft Visual Studio makes it easy to create your own controller class. It requires you to right-click
on the Controllers folder in the current ASP.NET MVC project and add a new controller class. In a
controller class, you’ll have one method per user action that falls under the responsibility of the
controller. How do you code an action method?

An action method should collect input data and use it to prepare one or multiple calls to some
endpoint exposed by the middle tier of the application. Next, it receives output and ensures that
output is in the format that the view needs to receive. Finally, the action method calls out the view
engine to render a specific template.

Well, all this work might add up to several lines of code, making even a controller class with just a
few methods quite a messy class. The first point—getting input data—is mostly solved for you by the
model-binder class. Invoking the view is just one call to a method that triggers the processing of the
action result. The core of the action method is in the code that performs the task and prepares data
for the view.

Choosing the Right Stereotype

Generally speaking, an action method has two possible roles. It can play the role of a controller, or it
can be a coordinator. Where do words like “controller” and “coordinator” come from? Obviously, in
this context the word “controller” has nothing to do with an ASP.NET MVC controller class.
These words refer to object stereotypes, a concept that comes from a methodology known as
Responsibility-Driven Design (RDD). Normally, RDD applies to the design of an object model in the
context of a system, but some of its concepts also apply neatly to the relatively simpler problem of
modeling the behavior of an action method.

Note For more information about RDD, check out Object Design: Roles, Responsibilities,
and Collaborations,
by Rebecca Wirfs-Brock and Alan McKean (Addison-Wesley, 2002).

RDD at a Glance

The essence of RDD consists of breaking down a system feature into a number of actions that the
system must perform. Next, each of these actions is mapped to an object in the system being
designed. Executing the action becomes a specific responsibility of the object. The role of the object
depends on the responsibilities it takes on. Table 7-1 describes the key concepts of RDD and defines
some of the terms associated with its use.

image

Table 7-2 summarizes the main classes of responsibility for an object. These are referred to as
object role stereotypes.

image

  In RDD, every software component has a role to play in a specific scenario. When using RDD, you
employ stereotypes to assign each object its own role. Let’s see how RDD stereotypes can be applied
to an action method.

Breaking Down the Execution of a Request

I’ve described some common steps that all action methods should implement. The responsibility of an
action method can be broken down as follows:

  • Getting input data sent with the request
  • Performing the task associated with the request
  • Preparing the view model for the response
  • Invoking the next view

Both the Controller and Coordinator RDD stereotypes can be used to implement an action
method—but they won’t produce the same effects.

Acting as a “Controller”

Let’s consider an action method in the apparently simple place-order use-case. In the real-world,
placing an order is never a simple matter of adding a record to the Orders table. It’s an action that
involves several steps and objects. It requires querying the databases to find out about the availability
of the ordered goods. It might also require an order to be placed to a provider to refill the inventory.
Placing an order typically requires checking the credit status of the customer and syncing up with the
bank of the customer and the shipping company. Finally, it also involves doing some updates on some
database tables. The following pseudo-code gives you an idea of the concrete steps you need to take:

[HttpPost]
public ActionResult PlaceOrder(OrderInfo order)
{
// Input data already mapped thanks to the model binder
// Step 1-Check goods availability
...
// Step 2-Check credit status of the customer
...
// Step 3-Sync up with the shipping company
...
// Step 4-Update databases
...
// Step 5-Notify the customer
...
// Prepare the view model
var model = PlaceOrderViewModel { ... };
...
// Invoke next view
return View(model);
}

Having all these steps coded in the controller at a minimum means that you end up with calls
made to the data access layer from the presentation. For simple CRUD (Create, Read, Update, Delete)
applications, this is acceptable; but it’s not acceptable for more complex applications.

Even when each of the steps outlined resolves in one or two lines of code, you have quite a long
and soon unmanageable method. The RDD Controller stereotype applied to ASP.NET MVC controller
classes suggests you should use the previous layout of the code. This is not ideal even for moderately
complex applications.

Acting as a “Coordinator”

The RDD Coordinator stereotype suggests that you group all of the steps that form the
implementation of the action within a single worker object. From within the action method, you place
a single call to the worker and use its output to feed the view-model object. The layout follows.

[HttpPost]
public ActionResult PlaceOrder(OrderInfo order)
{
// Input data already mapped thanks to the model binder
// Perform the task invoking a worker service
var workerService = new WorkerService();
var response = workerService.PerformSomeTask();
// Prepare the view model
var model = PlaceOrderViewModel(response);
...
// Invoke next view
return View(model);
}

The overall structure of the controller method is much simpler now. Solicited by an incoming
HTTP request, the action method relays most of the job to other components. I call these compo-
nents worker services; in RDD jargon, they look a lot like Controller objects and, in some simple cases,
they’re just service providers.

Fat-Free Controllers

ASP.NET MVC is a framework that is designed to be testable and promotes important principles
such as separation of concerns (SoC) and Dependency Injection (DI). ASP.NET MVC tells you that
an application is separated in a part known as the controller and a part referred to as the view (not
to mention the model discussed here). Being forced to create a controller class doesn’t mean you’ll
automatically achieve the right level of SoC, and it certainly doesn’t mean that you’re writing testable
code. As mentioned in Chapter 1, “ASP.NET MVC Controllers,” ASP.NET MVC gets you off to a good
start, but any further (required) layering is up to you.

What I haven’t probably stated clearly enough is that if you don’t pay close attention, you end
up with a fat and messy controller class, which certainly isn’t any better than a messy (and justifi-
ably despised) code-behind class. So you should aim to create controller classes as lean and mean
collections of endpoints and remove any fat from them.

Note According to my standards, I wasn’t precise earlier when I called Dependency
Injection a principle. More specifically, DI is just the most popular pattern used to
implement the Dependency Inversion Principle, according to which the surface of contact
between dependent classes should always be an interface instead of an implementation.
Much less known (and understood) than DI in the wild, the Dependency Inversion Principle
is the “D” in the popular SOLID acronym that summarizes the five key design principles for
writing clean, high-quality code.

Short Is Always Better

If you have a method that is about 100 logical lines long, that code probably includes 10 to 15 lines of
comments. Generally, 10 percent is considered to be a fair ratio of code to comments; I’d even go as
high as a comment every three logical lines if you want to make sure that you explain clearly the whys
and wherefores of what you’re doing and really want to help whomever deals with that piece of code
after you.

However, regardless of what you decide the ideal ratio is, my point is that a method that’s 100 lines
long makes little sense. You can probably break it into three or four smaller methods, and get rid of
some comments too.

I don’t call myself an expert in software metrics, but I usually try to keep my methods below 20
lines—which more or less matches the real estate available in the Visual Studio editor on a normal
laptop. 

How can you manage to keep the code of action methods as short as possible? Surprisingly
enough, applying the RDD Coordinator stereotype is what you must do, but even that’s not always
sufficient.

Action Methods Coded as View Model Builders

A method designed to be a coordinator invokes a method on a worker object, has some work done,
and gets some data back. This data should simply be packed into a dictionary, or a strongly typed
class, and then passed down to the view engine.

The worker class, though, is attempting to bridge the gap between the data model you have on
the middle tier—the domain model—and the data model you have in the presentation layer—the
view model, or the data being worked on in the view. (By the way, “the data being worked on in the
view” is the wording originally used in the MVC paper to define the role of the model.)

If the business objects you invoke on your middle tier return collections or aggregates of domain
objects, you probably need to massage this data into view-model objects that faithfully represent
the contracted user interface. If you move this work into the controller class, you’re back to square
one. The lines of code you cut off by using worker services and the RDD Coordinator stereotype are
replaced by just as many lines for building a view model.

To support your efforts in getting fat-free controllers, I recommend a strategy based on the
following points:

  • Relay any action to a controller-specific worker service class.
  • Make methods of the worker service class accept data as it comes from the model binder.
  • Make methods of the worker service class return data expressed as view-model objects that
    are ready to be passed down to the view engine.
  • Grab exceptions via attributes.
  • Use .NET Code Contracts for checking preconditions and, where applicable, ensure
    postconditions.
  • For anything else that requires a bit of logic, consider using custom action filters.

Let’s see how I envision a worker service class.

Worker Services

A worker service is a helper class that goes hand in hand with the controller. You might reasonably
expect to have a distinct worker service class for each controller. On the other hand, the worker
service is just an extension of a controller and results from the logical split of the controller behavior
pushed by the RDD Coordinator role.

I’m using the word service here to indicate that this class provides a service to callers—it has
nothing to do with any technology for implementing services. Figure 7-1 shows an architectural
perspective of worker services in ASP.NET MVC.

image

A worker service is just a matter of design, and design is design regardless of its complexity. So
you don’t have to wait for a giant project to experiment with these features. Let’s go through a simple
example that shows the power of the worker service approach. Admittedly, it might sound like a lot
of work to do for a simple demo, but in the end it costs you just an extra interface—and it scales
exceptionally well with complexity of the domain.

Implementing a Worker Service

You can start by creating a WorkerServices folder in your ASP.NET MVC project. Which folders you cre-
ate under it is entirely your responsibility. I usually go with one folder for each controller plus an extra
folder for interfaces. Figure 7-2 shows a glimpse of a project using this approach.

image

If you prefer, you can move the WorkerServices section to a separate assembly—it’s your call. As
mentioned, you create one worker service for each controller. For the Home controller, you can create
the IHomeServices interface and the HomeServices class:

public interface IHomeServices
{
HomeViewModel GetHomeViewModel();
}
public class HomeServices : IHomeServices
{
private IHomeServices _workerService;
public HomeViewModel GetHomeViewModel()
{
...
}
...
}

In the sample application we’re considering, the home page picks up a list of featured dates and
renders the time span in days between those days and the current day. On the middle tier, you have
a repository that returns information about featured dates such as the date, whether it is absolute
or relative (for example, February 8, regardless of the year), and a description for the date. Here’s an
example for a featured date object for a domain model:

namespace FatFree.Framework.DomainModel
{
public class MementoDate
{
public DateTime Date { get; set; }
public String Description { get; set; }
public Boolean IsRelative { get; set; }
}
}

The repository will likely fill up a collection of these objects when querying some database. At any
rate, the worker service gets a collection of MementoDate objects and processes them up to the point
of obtaining a collection of FeaturedDate objects—a type that belongs to another object model, the
view model:

namespace FatFree.ViewModels
{
public class FeaturedDate
{
public DateTime Date { get; set; }
public Int32 DaysToGo { get; set; }
public String Description { get; set; }
}
}

There are two operations that need be done. First, any relative date must be transformed into an
absolute date. Second, the time span between the given date and the current day must be calculated.
For example, suppose you want to calculate the distance to the next occurrence of February 8. The
target date is different if you’re computing January 2 or March 5.

Here’s a portion of the code in the worker service:

private IDateRepository _repository;
...
public HomeViewModel GetHomeViewModel()
{
// Get featured dates from the middle tier
var dates = _repository.GetFeaturedDates();
// Adjust featured dates for the view
// for example, calculate distance from now to specified dates
var featuredDates = new List<FeaturedDate>();
foreach(var mementoDate in dates)
{
var fd = new FeaturedDate
{
Description = mementoDate.Description,
Date = mementoDate.IsRelative
? DateTime.Now.Next(mementoDate.Date.Month,
mementoDate.Date.Day)

                                       : mementoDate.Date
};
fd.DaysToGo = (Int32)(DateTime.Now - fd.Date).TotalDays;
featuredDates.Add(fd);
}
// Package data into the view model as the view engine expects
var model = new HomeViewModel
{
Title = "Memento (BETA)",
MessageFormat = "Today is <span class='dateEmphasis'>{0}</s
Today = DateTime.Now.ToString("dddd, dd MMMM yyyy"),
FeaturedDates = featuredDates
};
return model;
}

What about the controller? Here is the code you need:

public ActionResult Index()
{
var model = _workerService.GetHomeViewModel();
return View(model);
}

Figure 7-3 shows the sample page in action.

image

As you can see, there’s no magic behind worker services. As the name suggests, they are worker
classes that just break up the code that would logically belong to the processor of the request.

Do We Really Need Controllers?

The code of each controller method will hardly be as simple as what I’ve shown here, which was
just one logical line. In real-world scenarios, you might need to pass some input data to the worker
service—perhaps use an if statement to quickly rule out some cases, or even further edit the view-
model object. This latter scenario might occur when you attempt to gain some reusability and get
one worker service method to serve the needs of two or more controllers’ action methods.

To flesh out the code in action methods, use exception handling, null checks, and preconditions.
In the end, to keep action methods lean and mean you need to push the RDD Coordinator role to the
limit and move any processing logic out of the controller.

Does this mean that you don’t need controllers anymore? Each HTTP request maps to an action
method, but you need some plumbing to make the connection. In ASP.NET MVC, the controller is just
part of the infrastructure, it shouldn’t contain much of your code and, big surprise, there should be
no need to test it. If you consider controllers to be part of the infrastructure, then you take their basic
behavior for granted; you need to test your worker services instead.

The Ideal Action Method Code

Let’s top off this discussion by analyzing an ideal fragment of code you should find in your action
methods. It uses attributes to handle exceptions and Code Contracts to determine preconditions:

[HandleError(...)]
public class DateController : Controller
{
private readonly IDateServices _workerService;
public DateController() : this(new DateServices())
{
}
public DateController(IDateServices service)
{
_workerService = service;
}
[MementoInvalidDateException]
[MementoDateExistsException]
[HttpPost]
public ActionResult Add(DateTime date, String description)
{
Contract.Requires<ArgumentException>(date > DateTime.MinValue);
Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(description);
var model = _workerService.AddNewDate(date, description);
return View(model);
}
}

In this example, custom exception attributes are used to catch specific exceptions that might be
raised by the worker service. In this case, you don’t need to spoil your code with ifs and null checks. (I
have nothing against using if statements, but if I can save myself and my peers a few lines of code and
still keep code highly readable, well, by all means I do that.)

Important As an attentive reader, you might have noticed that I completely ignored an
important point—how to get ahold of an instance of the worker service. And how does the
worker service, in turn, get ahold of an instance of the repository? Techniques and tools to
inject dependencies in your code is exactly the next topic. (And in the next chapter, I’ll say
more about injection points and related techniques in the entire ASP.NET MVC framework.)