This patterns cluster starts off simply with Model-View-Controller (MVC), a long-standing pattern that has stood the test of time when it comes to separating business logic from presentation logic. Although this pattern is not new [Buschmann96], this collection presents it in a simplified form that is tailored for building business solutions, not for building user interface frameworks for rich clients. The pattern is written first at the design level, and is then mapped to a platform implementation named Implementing Model-View-Controller in ASP.NET. Figure 3.1 shows the Web Presentation patterns cluster.
Figure 3.1: Web Presentation patterns cluster
The implementation of MVC with Microsoft ASP.NET starts with an example of a simple system, written on a single page, with application logic embedded in the presentation elements. As complexity grows, the code-behind feature of ASP.NET is used to separate the presentation code (view) from the model-controller code. This works well, until requirements drive you to consider reusing the model code, without the controller, to avoid redundancy within your application. At this point, independent models are created to abstract the business logic, and the code-behind feature is used to adapt the model to the view code. The implementation then finishes off with a discussion about the testing implications of this MVC approach.
So far, the use of the Model-View-Controller pattern has focused on the model and the view; the controller plays a relatively minor role. In fact, the controller at work in this pattern is really the implicit controller in ASP.NET. It is responsible for sensing user events (requests and postbacks) and wiring those events to the appropriate system response, which in this case is the events in the code-behind page.
In dynamic Web-based applications, many common tasks are repeated during each page request, such as user authentication, validation, request parameter extraction, and presentation-related database lookups. Left unmanaged, these tasks can quickly lead to unnecessary code duplication. Because these tasks have everything to do with sensing user events and determining the proper system response, the logical place to put this behavior is in the controller.
The next pattern in this cluster is Page Controller, which is a refinement of Model-View-Controller and is appropriate for the next level of complexity. This pattern uses a controller at the page scope, accepts input from the page request, invokes the requested actions on the model, and then determines the correct view to use for the resulting page. Duplicate logic, such as validation, is moved into a base controller class.
Implementing Page Controller with ASP.NET illustrates the power of the ASP.NET built-in page controller functionality with a common look-and-feel example. The implementation also uses the Template Method [Gamma95] pattern, in conjunction with Page Controller, to define the skeleton of an algorithm in an operation, deferring some of those steps to subclasses.
As more complexity is added to the application, eventually the page controller accumulates a great deal of logic in the base class, a problem which is often solved by deepening the page controller inheritance hierarchy. Given enough complexity, both of these factors lead to code that is hard to maintain and extend. Also, certain applications need dynamic configuration of navigation maps, which would potentially span multiple page controllers. When this level of complexity occurs, it is time to consider Front Controller.
Front Controller, the next pattern in this catalog, is also a refinement of Model-View-Controller. In a front controller, all of the requests are channeled through a single, usually, two-part controller. The first part of the controller is the handler, and the second part is a hierarchy of Commands [Gamma95]. The commands themselves are part of the controller and represent specific actions that the controller triggers. After the action has executed, the command chooses which view to use to render the page. Usually, this controller framework is built to use a configuration file that maps requests to actions, and is therefore easy to change after it is built. The tradeoff, of course, is in the level of complexity inherent in this design.
The last two patterns in this cluster involve filters and caching.
Intercepting Filter offers a solution to the problem of how to implement common preprocessing and post-processing of the HTTP request. An Intercepting Filter is an ideal place to perform common tasks that are not application-specific, such as security checks, logging, compression, encoding, and decoding. Intercepting filters are typically concerned with performing one particular task. If multiple tasks execute against the HTTP request, multiple filters are chained together. Implementing Intercepting Filter in ASP.NET Using HTTP Module highlights the ease with which you can implement this pattern in ASP.NET.
Page Cache deals with increasing the scalability and performance of Web applications by keeping a copy of often-used dynamic Web pages that are expensive to create. After the page is initially created, the copy is sent in response to future requests. Page Cache also discusses several key cache design factors such as cache refresh, data freshness, and cache granularity. Implementing Page Cache in ASP.NET Using Absolute Expiration demonstrates the built-in page cache functionality of ASP.NET.