DRAFT - DRAFT - DRAFT

Head: To MVC... and Beyond!

Subhead: Take your architecture as far as it needs to go (and no farther)

Abstract: Many experts advise moving your web application to a so-called MVC Architecture. While this is a noble goal, it is a bit simplistic. The original MVC Design Pattern was intended for use in an interactive GUI, and the many tweaks and nuances required before it can apply to the Web deserve closer examination. We discuss what needs to be removed, and what needs to be added, to the traditional Model-View-Controller design pattern. Finally, we cover a new variant called Multi-Model MVC. (If you want to cut to the chase, start with the section entitled, "Multi-Model MVC".)

Monolithic Servlet

One simple solution to the Web application design problem is to make each servlet responsible for all aspects of its request processing: reading parameters, fetching data, and formatting output. This conforms, more or less, to the infamous Big Ball of Mud design anti-pattern [Foote99]. In this article we will proceed to discuss more sophisticated architectures that resolve some of these problems.

I must stress that each of these architectures comes with a price. The more complex your architecture, the more difficult it may be to understand, and thus to maintain and improve. If your application consists of one servlet, and that servlet does something very simple, then by all means, leave it a ball of mud. If it will be easier to understand a single 20-line doGet routine than a five-object MMMVC cluster or a nest of XSLT files, then your choice is clear.

[Footnote: Some architectures use a single servlet as a gatekeeper or dispatcher, responsible for receiving all input; this gatekeeper performs certain common tasks (e.g. authentication) and then sends the request on to either another object (either a servlet, JSP, or POJO) for processing. This does not qualify as a Big Ball of Mud; rather, all the succeeding discussion about controller servlets and view servlets applies, only pushed down one layer. However, if you have multiple pages or operations in your application, and they are handled by a giant switch statement, then you are closer to a Big Ball of Mud.]

Pull Out the Data Model: the Document-View Pattern

One very powerful improvement to the Mud Servlet architecture is to separate out the code that accesses and modifies your data store. The set of objects now responsible for data is called the Data Model (often simply "Model"). This design technique is so well-known and widely-recommended that I will devote little space to it here.

Many GUI and Web architectures claim (or are claimed) to use MVC, when in reality they conform more closely to a different pattern, known as Document-View [POSA p.140, App89, Kru96]. In Document-View, the system is composed of two sets of objects: the Document, which represents all the data; and the View, which represents a particular UI -- including input, output, and controller-like functions.

By pulling your data model out into a separate object (or set of objects), you have taken a huge step towards model-view separation, and your application will be more scalable. There's no need to feel guilty about not going all the way. However, if you want to discover the advantages to further separating the responsibilities of your web application, read on...

Pull Out the Action: Solving the Dispatch Problem

At its heart, the Web is a series of pages. It is natural when designing Web page flows to construct one servlet per page; proceeding to the next page would then consist merely of calling the URL of the appropriate servlet.

Unfortunately, as the number of pages in your application increases, and as you add back-end form processing, this leads to problems. What if there is more than one way for the user to reach a given page? For instance, the home page will be the result of a simple GET, but it may also be the result of a form (say, "login") whose submission succeeds silently. If we keep the one-servlet/one-page rule, then the ACTION of that form must be the URL to the home page servlet; this would imply that the home page servlet know how to process forms, which is a bad idea, separation-of-responsibilities-wise.

A better solution is to invent an interstitial servlet, called an Action Servlet, one for each type of form or user action. The Action servlet never sends any output to the user. Instead, it reads the Request parameters, accesses or modifies the data model, and then dispatches the request to the appropriate View servlet.

This architecture is a fundamental breakthrough for Web applications. It saliently separates responsibilities between Form Handling and HTML Rendering. It allows multiple actions to result in a single view. It also allows the user to jump directly to the view page (via a bookmark, perhaps). Multiple views also simplify allowing multiple target platforms (XML, WML, SOAP, etc.) without duplicating the action logic.

It also simplifies error reporting, in that the Controller may select any view it likes. You can either use a dedicated error view servlet (perhaps telling the user to use the Back button to fix the mistake), or you can add a parameter to the View servlet instructing it to display an error message.

(Note that the Struts framework follows this convention with its *.do naming convention.)

Sidebar: Redirect vs. Forward

[TODO: Flesh out]

Forward:

Redirect:

Web MVC

Once we separate both a Data Model and a View Servlet, we reach an architecture that is typically [see J2EE Blueprints] called MVC.

This architecture is tremendously useful! Enough has been written about it elsewhere that I will not bore you with its virtues here. Suffice it to say that model-view separation is crucial for scaling an application.

You may notice that the View servlet also must receive user input (in the form of CGI parameters, session and cookie data, and so forth). It may even be forced to dispatch to an error view servlet. In other words, the View acts as a Controller! It appears that we have not truly separated the functions of Controller and View. Please be patient: we will discuss this paradox below (section Multi-Model MVC).

The Model-View-Controller Design Pattern: GUI vs. Web

The MVC pattern [see POSA] was developed for use in a point-and-click GUI environment. Although the Web is arguably a graphical user interface, it is not a GUI: it is quite different from the event-driven, instant-feedback world of MVC's origin. Although they share the same rough object model -- there are objects in each that can be called Model, View, and Controller -- they should be known by different names. For this article, I will distinguish them as GUI-MVC and Web-MVC.

In a GUI environment, small user gestures (like entering a single character into a text field) can have immediate UI feedback (like enabling or disabling buttons). In a Web environment, the typical event is on a much larger scale than envisioned by GUI-MVC. JavaScript notwithstanding, the servlet gets notified only when one or both of the following is true:

Each of these "events" is much coarser than those anticipated by GUI-MVC. Where before the controller was responsible for many nuances of user activity, in Web-MVC its role is essentially reduced to a single three-step algorithm: read the servlet parameters, decide which data methods to call, and decide which view to display next.

Furthermore, the traditional GUI-MVC pattern requires that all communication with the data model be through a formal observable-observer messaging API. That sort of apparatus would be clumsy, and probably overkill, in the less-than-real-time world of Servlets.

(Nevertheless, in Sun's J2EE Blueprints MVC diagram, "Change Notification" is prominent, despite the fact that no such mechanism exists in a Web application; also, although the J2EE Blueprints MVC Summary has a cluster of objects labeled "View," and another (in the EJB tier) labeled "Model," these are surrounded by a cloud of objects and JSPs that evinces a much more convoluted architecture than straighforward MVC.)

On the other hand, the Web-MVC pattern deals with several questions left unasked by GUI-MVC. While GUI apps are mostly modeless, Web applications suffer from complex rules determining the transitions from view to view. These nuances of page flow and navigation usually find their way into the Controller role of Web-MVC. In more sophisticated frameworks (e.g. Blueprints, Struts) there are separate actors to handle these navigational tasks: gatekeepers, Screen Flow Managers, page flow configuration files, and so on.

There are other minor, pedantic differences as well. For example, in GUI-MVC, the view creates its controller; in Web-MVC they are either independent, or the controller (servlet) creates a view (renderer).

Application Data vs. View Data

One

Multi-Model MVC

In the new Multi-Model MVC architecture, we take the servlet-as-view from Web-MVC and rip it apart, creating three new objects. Recursively, these three objects play the roles of Model, View, and Controller -- but on a smaller scale. All three Foo Page objects -- the FooServlet, the FooRenderer, and the FooData -- are considered part of the View in the larger Web-MVC picture. However, each of them plays a separate MVC role inside this MMMVC View Cluster.

Here, the Model (the Page Data objects) represents everything that will show up on the page, in what order, in what language, etc. The page model represents an overlapping set with the application model. It does not contain all the information in the application model, merely the subset that is necessary for the current page to render. However, it is not a subset, for it also contains other, additional information: for example, which user is currently logged in, or which page of search results is showing, or an error message based on an invalid field.

The Page Renderer objects, in turn, perform a transformation from the Page Data into HTML. The most logic performed by the Renderer should be no more sophisticated than simple conditionals or iteration on values and collections returned by the data model.

Finally, the Page Servlet (Controller) receives the parameters, creates an appropriate Page Data (Model), and passes it to a Page Renderer (View).

Full separation between Model and View is critical here. Many so-called MVC systems have been criticized for allowing program logic to "leak in" to the view. For example, JSPs allow arbitrary code to be executed inside scriptlets. Even custom tags can be used to perform direct data access.

In this architecture, the Page Data objects must prepare virtually everything required by the Page Renderer objects; the Page Renderers accomplish almost nothing but simple-minded rendering on their own.

Note that this architecture is applicable to many XSLT-based Web application frameworks (notably Cocoon). In these frameworks, the Page Data role would be played by an XML document, and the Page Renderer role by the final XSLT template responsible for transforming that XML document into user-visible HTML (or WML, etc.).

MMMVC: Pros and Cons

Aesthetics. Where Web-MVC assigns controller-like functions to the View Servlet, MMMVC truly separates these out into an object of its own (the Page Servlet) while leaving rendering to the Page Renderer. Also, MMMVC clearly and unambiguously separates the content of a page (the Page Model) from the presentation of that page. Since content/presentation separation is reportedly the holy grail for which all Web UI frameworks are questing, this seems like a natural step.

Testability. MMMVC is much easier to test than previous architectures. You can test that the correct page model gets generated by passing known parameters to the controller and testing the page model it returns. You can also test the renderer by passing a known page model and testing its rendered output.

Since UI testing is both more difficult and, arguably, less important (since errors in HTML rendering are more likely to be noticed by humans), you can reserve the bulk of your test cases for the page model. Indeed, some Agile programming teams either do not test the UI at all, or write only rudimentary UI tests; this shortcut is only desirable with a very thin UI layer on top of a full-fledged data model.

For instance, if a Menu page must display the Soup du Jour (which changes every day), the page must not do

Date today = new Date();
int dayOfWeek = today.getDay();
String soup = data.getSoupForDay(dayOfWeek);
out.print("Soup du Jour: " + soup);
instead, that functionality must be delegated to the Page Model. Why? Because otherwise, it becomes impossible to test that Thursday's correct soup is displayed... unless you're testing on Thursday.
out.print("Soup du Jour: " + data.getSoupDuJour());
Note that getSoupDuJour() (with no parameters) is not a member of the application data model. You must add it to the Page Model.

Swap out components. MMMVC makes it much easier to transition between incarnations of Views. For instance, once you have isolated your HTML-generation code into a pure Renderer object, you can replace it with a JSP (or a page templating engine of your choice, such as Velocity or XMLC).

Multiple target platforms. While the Web-MVC architecture also allowed multiple targets (SOAP, XML, WML, etc.), MMMVC makes it even easier, assuming that all targets will use more-or-less the same page data.

Added Complexity: Why do we need to go to this MMMVC model? Why wouldn't it be sufficient to leave all three functions inside the same view servlet or JSP? Or, couldn't we at least combine the Page Model and the Page View? The Page Model properties sound like they could easily be instance variables of the Page Renderer.

Component Multi-Model MVC

The MMMVC architecture as described only applies to an application with a single page. It can apply equally well to an application with many pages, or with nested views. Nested views are a common technique. A given HTML page usually consists of several high-level components (viz. Title, Banner, Navigation Bar, Body, Footer). Each high-level component can consist of many low-level components (viz. Form, Anchor, Paragraph).

Each of these components can be factored into a miniature Model-View dyad. (The controller is usually superfluous at this level.) This is the approach favored by the JFC Swing components.

Note that at a certain point, making a separate model is not worth the extra complexity. For instance, a Text Field component, containing properties only for its width and its default text, may well prefer to store these in instance variables. However, more complex components -- say, a widget that draws an entire form -- may well benefit from a separate Model class.

Again, this is similar to the Struts and Blueprints models. In those architectures, individual page components (Banner, Navigation, Footer, et al.) are each rendered by a separate JSP.

References

Tapestry http://tapestry.sourceforge.net/ http://www.onjava.com/pub/a/onjava/2001/11/21/tapestry.html Struts http://jakarta.apache.org/struts [Foote99] http://www.laputan.org/mud/
Last modified: Mon Oct 21 15:56:33 PDT 2002