I love me some frameworks. They bring structure and convenience to otherwise complicated problems. The most popular frameworks typically implement some form of MV*, where * represents Controllers, ModelViews, or Presenters. While a good portion of problems can be decomposed into that paradigm there are some which simply cannot.
Why I dumped MV* (in this one instance)
This was the case when I set out to build the front-end client for the Bulbstorm Engagement Platform (BEP), and it’s a problem I’ve encountered once or twice before in my career. BEP attempts to consume 3rd party API’s and present the resulting data appropriately, but ultimately the end-user decides both the data and how it will be displayed.
I spent almost a year trying to shoehorn the problem into MV* before I came to the realization that it was not a viable solution. I was dealing with an “amorphous model,” and that’s where it all fell apart.
All of the major frameworks take a model-driven approach. The model drives the view and the * usually makes that happen. So if you don’t have a well-defined model, you don’t have a well defined app.
One approach was to define a model for every resource I wanted to support, for every API I wanted to support. That doesn’t scale. I wanted ALL of the resources from ALL of the API’s. Greedy bastard. Desperate times call for desperate measures, so I dumped MV*.
I don’t want to go into detail about how I solved the model issue, rather, I want to explain how I used a non-MV* approach to solve a problem. Suffice it to say that the solution became trivial once I had abandoned the model-driven paradigm.
J2EE Core Patterns to the rescue!
Java Platform, Enterprise Edition or Java EE is Oracle’s enterprise Java computing platform. The platform provides an API and runtime environment for developing and running enterprise software, including network and web services, and other large-scale, multi-tiered, scalable, reliable, and secure network applications. —Wikipedia
I got turned on to the Core J2EE patterns while reading PHP Objects, Patterns, and Practice by Matt Zandstra. In his book, Matt demonstrates a super common approach to PHP apps using the J2EE patterns as a guide. I’ve come to call this a approach request-driven, and it is used by many if not all of the most popular PHP frameworks.
The request-driven approach mostly means that the URL drives the behavior of the application. Backbone and a few others do this with Routes, but it is fairly de-emphasized in comparison to server-side frameworks.
How a “request-driven” approach works
- A request is made, e.g. example.com/about
- The URL is parsed into a request object (Request) along with cookies and POST data. Think of the
$_REQUESTobject in PHP for a good reference
- The Request gets passed to the Intercepting Filter where it is munged into a suitable form
- The filtered Request is then passed to the Front Controller which loads any configuration data for the app and then decides what view to display based on the contents of the request
- The Front Controller calls the appropriate View with the Request object
- The View uses a View Helper or other helpers to retrieve additional data and augment the Request with it
- The View is rendered with the data stored in the Request
- When a user interacts with the page (button click, form submission, etc) the URL is changed and the process starts over again
The Request object is passed through to each layer of the application. In each step, data gets added to it, it get’s poked and prodded for data, and it stores messages related to the overall process.
I use the IF to grab all sorts of Request-specific info, for example, the user’s session state, the browser’s capabilities (mostly using Modernizr), and so on. It’s important to note that no processing occurs here. Just get some info and move on. If any errors occur during processing a message should be added to the Request object to be processed later.
The Front Controller (FC) is responsible for two things:
- Get application configuration data
- Decide what view to render, based on the Request data
We don’t want to get any configuration data until this point because technically, up until now, we don’t have a full Request object. But once all that is done we can start to actually initialize the application. This process includes parsing the Request data to determine what view to render.
Were there any messages logged? Call the error view. Was auth required but the user doesn’t have a valid session? Call the auth view. Is everything as it should be? Show the index view. This is the only part so far where any logic takes place.
The view is really stupid. It takes some data and passes it off to a template engine (such as Handlebars). If more data needs to be collected that is specific to this view, you load up a View Helper. The View Helper can do things like AJAX. This keeps the view itself very light and the more complicated stuff re-usable.
So that’s it. The whole process starts over again when the user interacts with the page. The upside here (over a traditional app) is that we can handle click events and form submissions without reloading the page. I defer to a separate set of Intercepting Filter/Front Controller pairs for this kind of interaction though. It’s out of scope for this post, but one could imagine how the process is similar: create a Request (maybe an EventRequest for clarity), filter it, then resolve it to something that fires the desired action.
The missing link, GoF Design Patterns
Turns out that most of the J2EE patterns are just built-in versions of some common GoF design patterns. The Intercepting Filter is just the Decorator pattern and the Front Controller is the Command Pattern. A Request (the decorators component class) gets decorated, then a command (the view) gets passed the decorated object when it is executed.
The good and the bad
Obviously there are trade-offs to this approach. On the positive side:
- You end up with a highly-decoupled application
- It’s easy to test discreet objects/patterns
- Views can be loaded as-needed (if using AMD/RequireJS) to reduce the initial download
- The pattern is tried and tested, meaning: It’s been around for a long time.
On the downside, however:
- You end up with a lot of objects (especially if you take the classical route, as I have)
- There’s a tendency to duplicate code in views. Fortunately, it’s easy to spot and fix. If you see duplicate code, add it to a view helper
If you’ve got a hard-on for patterns, love the traditional approaches from languages like Java and PHP, and are having a hard time fitting your app into an MV* framework, this approach may be for you. It’s been working really well for me.