Brian's Waste of Time

Fri, 05 Nov 2004

MVC, Model 2, Java WebApps, (and callcc, why not)

Brief history of MVC and its convolutions, so I can point people to this in the future ;-)

Smalltalkers fought for years over the details of the right way to do this but the basics... UI (view) intimately knows the Model. It can watch it, and pull any values from it, but it cannot change it. The View also knows the Controller intimately. It uses it. That is right, the view is tightly coupled to both the controller and the Model.

The Controller knows the Model intimately. In the Smalltalk world the controller was just a bunch of opaque delegates which forwarded messages on to things in the model. The Controller is tightly coupled to the Model.

The Model represents the problem domain. It doesn't know a thing about the View or the Controller. The model is capable of being observed (whether observation is fine grained or course grained is a source of deep fighting, and really depends on the cost of examining the model, imho). When the state of the Model changes, it fires off messages to observers saying that it has changed. These observers are (usually) Views which then know they need to update what they are displaying.

Implementing a view typically involves subclassing an existing View class which knows how to draw what you want to draw. In the subclass you provide the plumbing required to register the instance to redraw itself when the model signals that it's state has changed, and you point intersting events (mouse click, etc) at the Controller. If you change your model, you will probably need to change your Views.

Implementing a Controller means providing receptacles for interesting messages to come in, and mapping them directly out to interesting knobs on the Model. The Controller is really really thin.

Along comes NeXT, Objective-C, and a variant on MVC. Those spiffy NeXT people got tired of subclassing view components, and decided that really powerful views should be easily reusable. They therefore made the default views smarter, and added hooks for pushing data into the View rather than having the View pull data from the Model. Furthermore, they had the View advertise the messages it can fire, making the View into a black box. In fact, they made it sufficiently black-boxish that there was, and still is, a market for 3rd party view components which could be hooked to arbitrary controllers.

The NeXT Controller, on the other hand, got much more complicated. It now is an observer of the View, and an observer of the Model. The View observation is fine grained (mouse click in WhoozatScreen) and the Model observation can go on about its holy wars over fine or course grained. The Controller is now dependent on the View, and dependent on the Model. It observes the View and when interesting things happen which require updating the UI, feed that information back into receptacles on the View.

The Model is no different, conceptually, than in the Smalltalk MVC design.

Developing in NeXT MVC means writing big fat controllers, but these controllers are still, mostly plumbing. They are just more complicated plumbing as they need to understand the View and push appropriate information into the View, as well as forward messages on to the Model. Nice tools (InterfaceBuilder in OS X is a direct descendent, iirc) were created to make this pretty straightforward, and, by reports, worked well enough that NeXT still has a cult following.

Then the Web happens and Sun starts talking about Model2 in terms of MVC. Model2, basically, attempts to recreate NeXT MVC for a crappy user interface (the web). If we take the canonical Model2 system, Struts, the web request comes into the Controller and is mapped to an Action. The Action needs to do the plumbing of the NeXT controller -- fire the interesting message into the Model (your domain model), and fire the interesting data into the View (your jsp^H^H^H velocity template).

Because jsp's and velocity cannot exactly receive messages with the interesting data from the model, that data is bundled up into the FormBean and passed in, where it can be used to fill out template slots. Similarly, as the template cannot easily (well it can, but it is icky) be interrogated for its state in a useful way, the form bean is used to pass information from the interesting ui events into the controller. It has a dual role.

This caught on, but people didn't like it much. There are very few fully fleshed out, black box, useful, user interface components for the web. Worse, there is no way for those components to hold state (ie, not change until they need to). This means that all of the information needed to re-render the user interface needs to be passed to the templating system every time the user interface is updated (every page load). You cannot really use nice drag-and-drop InterfaceBuilder stuff with JSP's (yes, there are some things that can, we'll get to that), and pulling the interesting bits out involves building a model that will be almost the same as the underlying domain model. So people tend to just use the domain model in their View. It is much easier than creating a seperate DTO layer.

So the resulting application, which is supposed to look, logically, just like the NeXT MVC system, actually has a dependency from the View back to the Model. This is so much more useful and powerful than adding another mapping layer to map from Foo to VerySlightlyDifferentFoo that applications do it. It is almost always easier to change the view template when the model changes then it is to create the mapping layer, and change the mapping layer when the model changes.

Now, we have, recently, a few more tools, which conceptually map back to the NeXT MVC style. The forerunner here is probably Tapestry, but JSF and RIFE are scrappy contenders (WebObjects, from NeXT^H^H^H^ Apple actually had the first popular web tool here). The core problem with the Model2 system was that there were no View components. There was a template which could hold no state, which had to be logically rebuilt every time, and which usually had to be totally aware of the model in order to be anywhere near productive.

These tools have the NeXT concept of the rich view component which is reusable across applications, a fat controller which handles pushing data around, and a domain model which can model the problem domain without being aware of anything else. Nice. Nothing has come close to the beauty of InterfaceBuilder yet, but JSF vendors are trying. Tapestry seems pretty hooked on XML instead of GUIs, but it should be amenable to a a GUI, and some people actually prefer XML.

All of these models are based around interactive, event-driven (menu, mouse click, etc), user interfaces. The web happens to be workflow oriented (page driven). A great many applications benefit from event-driven design, but a workflow (request == transition) oriented user interfaces fit, in many cases (not all), more logically into the request/response page model of the web (see gmail for a brilliant non-workflow web app). Workflow user interfaces, in the rich client world, involve things like wizards. Various tools attempt to model this (Seaside, Cocoon, Struts-Flow, RIFE (actually RIFE and Cocoon have the rich component idea too!)) and the there is a lot of variety in design there, yet.

System design in this world is quite different from system design in event-driven apps. Pick the right tool for the job =)

4 writebacks [/src/java] permanent link