Scott Gu has a post describing the ASP.NET MVC framework that he presented on an altnetconf and you can see the video of that presentation on Scott Hanselman’s site. A lot of the noise has assumed that you comprehend what that means, and why some folks are jazzed about it, particularly the ALT.NET gang. But on the off chance that MVC frameworks have eluded you so far let’s talk about what they are and why they are important.
Separation of Concerns
The MVC in the acronym stands for Model-View-Controller. MVC is a pattern for separating what you are representing from how you are presenting it:
· The View: which knows how to render the UI
· The Controller: which knows how to initialize the UI and react to actions.
· The Model: which encapsulates the information we are interested in displaying.
By separating these different concerns we make it easy to maintain them. Elements adhere to the single-responsibility principle: an object has one and only one reason to change. For example, if we want to change the way we render our elements, but not how we persist or calculate those elements, we don’t have to touch the model. This reduces risk: we do not have to modify code that is unrelated to our changes, and thus risk introducing errors in parts of the system that are orthogonal to the alteration we are making.
Classic forms based programming tends to mix everything together in the event handler: in response to a new price being entered, the code within the event handler calculates a new yield and sets its value on a label on the form. The problem here is that if we want that algorithm elsewhere, we have to first separate it from its interaction with the UI widgets. Often what happens is that instead, the algorithm is repeated elsewhere in a different form. Now when we want to change the algorithm we have to do it in two places. MVC tries to break the high-coupling between these separate concerns.
We use an abstract type (interface or virtual methods) for the controller, view, and model, so that we can provide new implementations of any one element without modifying the others. This is the open-closed principle: software entities should be open for extension, but closed for modification.
You might use this facet of MVC to provide alternative views of the same data, say a graph and a spreadsheet. MVC allows you to swap provide additional implementations because it encapsulates its display in a view.
Because MVC encapsulates the response mechanism in a controller object, it also allows you to change the way a view responds without changing its visual representation, for example to make a view read-only for low privilege users, or to provide a different workflow for different user roles.
This ability to provide many concrete implementations of an abstract type, polymorphism, is an example of the Strategy pattern. A Strategy is a type that represents an algorithm, and by providing new implementations of that type you can vary details of that algorithm or its internal data structures.
Variations on a theme
There is smorgasbord of terminology used for MVC variants, such as Model-View-Presenter or Application Controller. Fowler has a pretty good summary of the various flavors and phrases like Supervising Controller, Passive View, and Presentation Model. The variation is mainly around view-model interaction. For example Supervising Controller recognizes the presence of data-binding frameworks and so admits view-model interaction to refresh data. The Passive View has the controller mediate all interaction, and as a result is easier to test. The Presentation Model takes state and behavior out of the view into one class –a controller-model combination.
The key is to recognize the common thread: these patterns strive for a clean separation of concerns between what we are representing and how we represent it.
The observer pattern is a common adjunct to the separation when you have multiple views displayed simultaneously, such as in an MDI application. The model is an observable, and the view or controller can subscribe by registering an observer interface. If the model is updated outside of the current view, for example by a rate feed, then the views are notified to render themselves afresh. However, it’s not relevant in the presentation tier for a web application where you cannot push updates to the browser. Passive View is a variation of MVC where the controller is responsible for synching state between the model and the view, and does not rely on the Observer pattern, so it’s a common choice for web applications.
MVC also tends to support the notion of composite views, a view which itself contains other views, all of which may have their own controller and model. So your page is a view, but all of the controls on the page are also views. MVC identifies this as a composite view. A composite view may be used wherever a view can, but also delegates to its child views, which, of course, may in turn be composite. Control trees in UIs are in expression of this idea of composition.
The Model-View architecture has been a particularly recurrent variation. In this form the controller and the view are merged into one. It has been particularly prevalent in frameworks to support event-driven programming on windowing systems. MFC’s document view architecture is a classic example here; the form both knows how to render the controls and how to respond to events. The loser here is testability – to test the controller we need to instantiate the view.
A related danger here is the temptation to merge the model in as well, which is the anti-pattern of business logic in the UI. This is especially true when there is no tradition of separating out the model, such as in forms based development.
Some people don’t like the view talking to the model and try to improve the separation by using a Data Transfer Object between the view and the model. For my part as long as the view does not embed the business logic, but leaves it within the view, and is passed the model instead of holding a reference to it, I’m prepared to let the view access the model’s state. I don’t think that the protection of a DTO is always worth the cost. YMMV.
The view embedding behaviors that ought to be methods on the model and the view calling those methods and not the controller are the real smells we want to avoid.
View-Controller in WebForms
Within WebForms we handle any HTTP request in the code-behind, so we can identify it as having the responsibilities of the controller. The template code gives us the layout of the html we return in the response and represents the view. However, the code-behind file and the template are coupled, because controls declared on the aspx become fields in the code-behind, so there is no true separation between them. So within WebForms the built in model is Model-View and not Model-View-Controller. No shock here, perhaps, as the design goal was to move the forms rich-client programming model server side.
Unfortunately we get very poor testability from a view/controller, because we cannot test the controller logic in isolation from the view. This is a problem because in order to instantiate a view we need to spin up whatever hosts the view: in this case we need to launch the asp.net pipeline. This is difficult to do within most xUnit test runners, and even where it is supported, for example by MSTest, the tests are slow and fragile.
Types of Controllers
In a web application when the web server (IIS et al.) forwards our application an HTTP request we need to provide a response. Under ASP.NET an implementation of IHttpHandler forms the end point for the request and sends the response.
Within WebForms, Page derived objects implement of IHttpHandler and are the end points for requests.
The key takeaway is that there an affinity between controller and page. Each page has one controller, each controller has one page. Fowler calls this a Page Controller and it is WebForms architecture. Note that although we have a controller, we are still implementing model-view with WebForms because the controller and view are coupled.
A Page Controller makes re-use hard. If several actions lead to the same view being displayed, but with differing data we end up with a lot of conditional logic in the page controller or duplication between cut & paste variants of the same page. A page controller also makes navigation hard, for example when coding a wizard, because the logic is embedded within a page, not between the pages, again leading to re-use.
There are other models, particularly Front Controller and Application Controller where the view-controller relationship is broken and controllers dispatch requests to the desired views, after first deciding which view and which data.
There is only ever one Front Controller, which decides what action to call in response to the request.
There are potentially many application controllers, to which the front controller forwards a request, which decide what action to take and redirect to the required view.
Front and application controllers make it much easier to orchestrate navigation within your application, because the navigation code no longer lives within the forms. Instead navigation is handled by the controllers.