Click here to monitor SSC
  • Av rating:
  • Total votes: 34
  • Total comments: 7
Dino Esposito

Never Mind the Controller, Here is the Orchestrator

11 October 2011

The Model-View-Controller pattern of the ASP.NET MVC allows the separation of the business logic  from the input and presentation logic. Although it permits the independent development, testing and maintenance of each component, it doesn’t guarantee clean code. Dino Esposito offers a modified approach in which an Orchestrator component helps to keep your controllers small, clean and manageable.

The Model-View-Controller (MVC) pattern is not by itself a guarantee of clean code and neatly separated layers. The controller simply ensures that any requests are routed to a specific method that is responsible for creating the conditions for the response to be generated and returned. The key question is this: ‘Are you sure that all the responsibilities that are thereby assigned to the controller method make for a thin layer in which simplicity and the ‘single responsibility’ principle are honored?’

Even in moderately complex applications (10-15 use-cases and some 30 domain entities), the steps that each controller method has to take in order to trigger the view engine are numerous enough to produce rather bloated code. In this regard, a controller class often ends up as a god-object-like sample code-behind class, padded with too many lines of code and nowhere near any ideal standard of testability and readability.

This article intends to show a slightly different approach that reduces the role of the controller to that of a mere part of the ASP.NET infrastructure, and introduces a new, more specific component, called the orchestrator: This component ties up the controller with the application logic.

Beyond Controllers

The primary message of this article is that, as a developer, you should do all you can to keep controllers as simple and thin as possible and move any code that implements application logic out of the controller class. The orchestrator class is a new class where the application logic should go. The controller just grabs the data from the presentation layer by whatever means (forms, query string, URL), and pass it to the orchestrator. The orchestrator then handles the request, which may involve changing the representation of data (from presentation model to domain model) and placing calls to the middle tier.

When I bring this argument to the table, I often receive the following objection: “Do you mean we should take any logic out of the controller to another class? What’s the point of transforming the controller into a pass-through class?”

I see two reasons for taking code out of the controller class. One is to introduce an explicit decoupling layer between controller and backend. This layer, you can call it the application layer, orchestration or just “service layer” after the underlying pattern, represents an explicit implementation of the various steps that a given use-case requires. In this way, from the beginning you reason in terms of your use-cases and create the classes that produce a better implementation. While focusing on the actions required by a given use-case, you can clearly see the data that your methods need to receive, and to return. In doing so, you reason in terms of plain data types, data transfer objects or,( to use an ASP.NET MVC terminology) view model objects. The methods of orchestration classes are therefore invoked from within controller methods and receive, and return, plain data transfer objects ready to be served to the view engine so as to produce the final HTML view.

Where am I heading here? I’m just getting towards the second reason for moving any significant code out of the controller class. You have no need to mock up the HTTP context. Any invoked controller method, in fact, will extract out of ASP.NET intrinsic objects (e.g., Session, User, Request, Cache) any significant data and pass it to orchestration methods as standalone data.

In this way, you need to focus your testing efforts on the orchestration classes rather than controller classes. Orchestration classes are entirely out of the ASP.NET context and can be easily tested in full isolation.

Skeleton of a Controller Class with Orchestrators

I see a 1:1 correspondence between controller classes and orchestrators. However, at the design level you first focus on orchestrators and then infer any controllers you need from there. If needed, at this point you also fix your URL routes in the global.asax file.

Determining the number and type of controller classes is always a critical design point for ASP.NET developers and architects. You mostly figure them out of use-cases anyway, but I’ve found out that shifting the focus to orchestrators and use-cases makes it a more natural process. On the other hand, you’re going to write some code that solves a specific use-case. Hence, you should focus on the logic required by the use-case; not a mere (yet important) part of the infrastructure such as controller class. Here’s some basic code that illustrates the structure of a controller with an orchestrator. As mentioned, I usually envision one controller per orchestrator.

public class SampleController : Controller
{
   private readonly ISampleOrchestrator _orchestrator;
   public SampleController() : this(ISampleOrchestrator orchestrator)
   {
   }

   public SampleController(ISampleOrchestrator orchestrator)
   {
      _orchestrator = orchestrator;
   }

   // More code here ...
}

Because the controller class clearly has a dependency on the orchestrator, you might want to design the controller class to support dependency injection. Therefore, as a first step you abstract the orchestrator to an interface. The code above uses the poor man’s dependency injection schema (an overloaded constructor); feel free to rewrite that code to use your favorite IoC framework.

Before I go any further with the description of a possible implementation of controller methods, let me share a thought about the use of dependency injection in this particular case.

It’s known that writing clean code costs you a few extra lines and a few extra brains cycles. For this reason, most developers in a constant hurry often decide that some dusty and lightly dirty code is acceptable. This is a deadly sin if you introduce less-than-clean in critical places. It is a forgivable sin if you don’t code against an abstraction where it is not strictly required. The bottom line is that you don’t need clean code everywhere. You don’t need to break apart just any dependency your classes may have.

I’d say that you don’t strictly need the burden of dependency injection in this particular context. The controller will always work with an orchestrator; but the resulting code in the controller class is really trivial and may not need testing at all. In addition, there are high chances that you won’t be reusing the orchestrator anywhere else in your code. In this case, a form of tight coupling between the controller and its orchestrator doesn’t look like a deadly sin.

Implementation of Action Methods

The implementation of action methods in a controller backed by an orchestrator is very minimal. It consists of collecting any data the orchestrator requires and placing a call to an orchestrator method. The orchestrator method returns a view model object that is ready to be passed down to the view engine. In its simplest form, here’s a possible structure for an action method:

public ActionResult Index()
{
    var model = _orchestrator.GetHomePage();
    return View(model);
}

As you can see, the controller method is a mere pass-through, because all the work is being done by the orchestrator. There’s really no need to unit test such a controller. Unit testing, instead, is a key element for the orchestrator. The good news, however, is that the orchestrator doesn’t have any sort of dependency on the ASP.NET runtime and there’s no need to mock up the HTTP context.

Note: There’s a good chance you’d still want to do some integration testing or system/end-to-end testing

Thanks to the redesign of part of the ASP.NET runtime that has been done with ASP.NET MVC, is no longer a mission-impossible task to mock the HTTP context. However, I would say that it is not quite a walk in the park: the deeper you use Request, Session or Response the harder it may become to write appropriate unit tests for controller methods.

Let’s consider a scenario in which you need to process some session state data in order to produce the next view—in an e-commerce application at some point the user clicks to proceed to the checkout page.

public ActionResult Checkout()
{
    var cart = Session["ShoppingCart"] as ShoppingCart; 
    var model = _orchestrator.ProceedWithCheckout(cart);
    return View(model);
}

The ProceedWithCheckout method gets an internal ShoppingCart object that is filled with any goods the user has added. The shopping cart object is usually stored in the session state; extracting it from session and passing it around as an independent object removes the need of mocking the HTTP context when testing the actual logic behind the checkout operation.

More in general, I’d say that the controller method is responsible for retrieving any data the orchestrator needs from whatever source it has access to. Being part of the ASP.NET MVC infrastructure, the controller has total access to runtime objects; by extracting data from there it can decouple the real logic of the action from the surrounding environment. Here’s another example:

public ActionResult GetNews(DateTime startDate)
{
    var user = User.Identity.Name;
    var model = _orchestrator.FindNews(user, startDate);
    return View(model);
}

In this case, the action method receives an input argument that has been properly processed by the ASP.NET MVC model binder object. (The model binder is an ASP.NET subsystem that maps query string values, form values and specific URL tokens to declared parameters of the action method. The match occurs on the name of the parameters.) Any input for the controller method is, one can reasonably assume, a good input for the orchestrator too. In addition, the controller can add some more data from intrinsic objects. In the preceding code, the action method receives a date and is expected to generate a view in which all news fresher than the specified date are listed. However, the use-case also requires that articles are in some way personalized and filtered by the preferences for the currently logged user. The controller selects the name of the current user from the User intrinsic object and passes that information to the orchestrator.

Inside an Orchestrator Object

An orchestrator class is a collection of methods that return a view model object—data as being worked on in the next view—and accept data taken from the request URLs or the HTTP context. Each method performs a number of operations as required by the use-case that the orchestrator addresses. This logic can be as simple as plain CRUD or, on the other hand, it could be so complex as to require a workflow and orchestration of external services, domain services, data repositories and whatever else you may have in the application’s backend.

The orchestrator object receives and returns data as the presentation layer expects it, but internally it may need to deal with data using a different representation, the domain model. The M in MVC, model, is merely about the data the view works on. This is not necessarily the same model you use to manipulate the data in the business layer.

If your application is entirely built using the Microsoft stack, then you may have an entity object model to persist to some database via Entity Framework. This means that the orchestrator will either call directly into the Entity Framework object context or use the services of a repository class. Along the way, the orchestrator may need to sync up with other Web and WCF services, external applications and ad hoc modules.

Most of the time, testing the orchestrator is vital for the development of the application. For this to happen, you should endeavor to make any dependency injectable on repositories, services, and whatever other module it is required.

In situations in which the orchestrator is simple enough to only require a database operation, whether it’s a query or update, you may even decide not to test it with unit tests, but to proceed directly with integration tests.

Finalizing Controller Methods

When you use orchestrators in a very simple use-case, basically a plain CRUD with no extra logic around, then you may find yourself dealing with a very basic implementation of controller methods:

public ActionResult Index()
{
    var model = _orchestrator.GetHomePage();
    return View(model);
}

In the real world, even in this simple case you will likely need more code around for things like exception handling and preconditions. Exception handling is often resolved via attributes on the method; preconditions can be effectively placed via contracts, as below:

[SomeException]
[SomeOtherException]
public ActionResult GetNews(DateTime startDate)
{
    // This check here is just an extra: this check belongs
    // especially to the domain model.
    Contract.Requires<ArgumentException>(startTime.Year >= 2010);

    var user = User.Identity.Name;
    var model = _orchestrator.FindNews(user, startDate);
    return View(model);
}

Exception handling can be implemented by a direct try/catch block, but attributes contribute to keep the entire method clearer to read. The same can be said for preconditions. Preconditions on a method are essentially a sequence of if-then-throw statements placed at the beginning of the method. Using contracts in .NET Framework 4 is functionally equivalent and enables your code to work with static analysis tools.

Summary

Even though ASP.NET MVC is sometimes perceived as the framework that allows you to write better code, that’s not necessarily true. The controller class is only a more pattern-friendly version of the old faithful code-behind class. Writing clean ASP.NET MVC code is still up to you!

Dino Esposito

Author profile:

A long-time trainer and consultant, Dino is the author of many popular books for Microsoft Press for .NET developers.including “Architecting Mobile Solutions for the Enterprise“ and “Programming ASP.NET MVC” both for Microsoft Press. CTO of Crionet, a firm specializing in Web-based and mobile solutions for sport events across Europe (http://www.crionet.com), at the moment Dino is also technical evangelist for JetBrains, where he focuses on Android and Kotlin development, and member of the team that manages WURFL—the database of mobile devices used by organizations such as Google and Facebook. Follow Dino through his blog at http://software2cents.wordpress.com or at http://twitter.com/despos

Search for other articles by Dino Esposito

Rate this article:   Avg rating: from a total of 34 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Right on!
Posted by: Emmett Childress (not signed in)
Posted on: Tuesday, October 18, 2011 at 7:38 AM
Message: There needs to more information concerning this topic on the internet. I have been using the interface-based ViewModel approach to keep controllers clean. Testing is less difficult and doesn't require new harnesses. Kudos for passing a strongly-typed object from the session.

Subject: Mistake?
Posted by: Chris Marisic (not signed in)
Posted on: Tuesday, October 18, 2011 at 8:09 AM
Message: When you wrote:

public SampleController() : this(ISampleOrchestrator orchestrator)
{
}

did you really mean

public SampleController() : this(new SampleOrchestrator())
{
}

I assume you meant simple poorman's dependency injection and just was typing too fast :)

Subject: Orchestrators and View Models
Posted by: Justin Rusbatch (not signed in)
Posted on: Tuesday, October 18, 2011 at 1:19 PM
Message: I agree with most of the practices you're advocating in this article, except that an "orchestrator" should return view models to the controller. In some cases, I return data in a different structure depending on the content type requested by the browser. It feels right for the controller to know how to transform my model into the appropriate structure based on that information.

Subject: Controller's ViewData and UrlHelper
Posted by: Anonymous (not signed in)
Posted on: Wednesday, October 19, 2011 at 12:34 PM
Message: Maybe I'm missing the point of this article but I would like to expand this article by introducing the idea of passing the controller's UrlHelper and ViewData to the orchestrator in the controller's constructor. This way the orchestrator can still have access to the UrlHelper and ViewData without having them being passed in every method of the orchestrator.

Subject: Do we need still another layer of abstraction?
Posted by: ulu (view profile)
Posted on: Thursday, October 20, 2011 at 3:38 AM
Message: Yes the controllers are sometimes overbloated, but moving the significant part of the code to another class you'll get.. overbloated orchestrators. You better convert your controllers to what they're meant to be.. the orchestrators! You can read the Asp.Net specific values (like CurrentUser) in your ModelBinders and have your controllers pretty much Asp.Net ignorant, except for having to return an ActionResult.

Subject: codes
Posted by: aajay (view profile)
Posted on: Thursday, October 20, 2011 at 11:27 AM
Message: Please share code so I can understand clearly
thanks,

Subject: great solution
Posted by: jeljeljel (view profile)
Posted on: Thursday, November 17, 2011 at 7:44 AM
Message: This is something I will start using immediately. Thanks for posting!

 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.