If MVC Is So Simple, Why Does It Get Screwed Up So Much?

Seems to me that there are lots of developers out there who have taken up the MVC banner, and charged forth into battle; silently crying out, “MVC! MVC! All apps should be MVC!” Or maybe it’s just me that did that… 🙂 Regardless, over the last few years, lots of people have adopted the MVC design pattern as their pattern of choice. But from what code I’ve seen, especially in the JavaScript world, most of it plain shit as far as following the pattern is concerned.

The most egregious guffaws are confusing object roles in an MVC system. I don’t know how many times I’ve seen objects that cross the Controller-Model or Model-View or View-Controller boundaries; intermingling roles within a single object. Other things I’ve seen are people defining objects to fill a particular role, then do all communication via two-way direct reference between objects. Or worse yet, I recently saw some code where the developer was having a controller make his view trigger an event to which the same view was the only listener! Yikes!

One could just call it stupidity on the part of the developers, but I’ll be more forgiving and say that all that boils down to simple ignorance of how the objects – and more importantly, how objects communicate in any MVC system – should work; or just plain ignorance of what MVC is all about.

The Model-View-Controller paradigm at its most pure is quite simple: It’s all about separation of concerns, and consigning “concerns” to three, specific classes of objects – Model, View and Controller objects. Each class has a specific purpose and role. In this installment, I’m going to talk about each of these classes, and how they should be used to build successful MVC applications.

Model

A Model is an application’s representation of the underlying data set; in other words, it’s data. It has accessor methods in the form of getters and setters to manipulate the data. It does not or should not contain business logic. Unfortunately, some 3rd-party libraries such as Backbone.js muddy the waters a bit by adding things like the “validate” method to allow data validation within the context of the model. I’ve always found this to be just plain wrong. While you certainly can put logic in the model by virtue of JavaScript not having any barriers to doing so, muddying the waters in the model’s role in this respect I believe does more harm than good.

To me validation is really the job of the controller, which should be the one that “owns” the business logic. However, I’m also in favor of placing validation logic in the view to encapsulate it and remove some of the burden from the controller, since validation is really view-specific.

A model’s role is simply to store runtime data. Consumers of the model can get or set attributes on the model. But when that data changes, the model is only responsible to notify listeners that it has changed. To me, that’s it.

View

To me, there are two types of Views: Smart Views and Dumb Views. Smart Views have a bit of logic in them, namely input and output validation logic to make them “smart” per se, but as the endpoint objects – that is, the objects that clients actually “see” – they should never contain core business logic. Actually Smart Views are simply Dumb Views with some validation. Some have argued that a Smart View could also be one that includes Model functionality, but I don’t subscribe to that at all. I think object roles should be distinct, and Views are responsible for presentation of data contained in their associated model. Period. As for “Dumb” views. They simply exist to display model data (and update the model if they’re used for input), and update themselves when the model changes. Pretty straight-forward.

But with any view, I found that following the following rule-of-thumb has saved me countless hours of anguish, and that is simply: A View knows about its DOM and its DOM only. It knows about no other View’s DOM. This is extremely important to consider, especially if you’re using Backbone.js whose views are normally attached to existing HTML elements, and not blitting out their own HTML. When you create a Backbone view and assign its “el,” you have to make sure that no other view – as you can potentially have several views on a page – can manipulate DOM represented by that “el.”

Admittedly, when I was new to Backbone, coming from a system I helped create whose views all contained their own HTML, I broke this rule because hey! jQuery lets you get the reference to any HTML element so long as it’s in the DOM tree. But I ran into a bit of trouble when I had multiple views on a page accessing the same region of the page and using the same el. It was a nightmare to try to maintain. I no longer do that, as I’ve learned that lesson, but mark my words: Your view should only be responsible for a distinct “el.”

Controller

Especially in JavaScript, a Controller is almost superfluous, as it is mostly used as an instantiation device for models and views. But it can carry “safe” business logic; that is, business logic specific to the operation of the application that doesn’t expose trade secrets. It is also used to control application flow. Circling back to Backbone.js, in earlier versions of Backbone, the “router” object was called controller, but was later renamed to router. I think this was a smart move because a router is simply a history manager wrapper. It doesn’t control application flow. That’s the controller’s job.

For instance, I’m currently building an application  where I have a master controller that instantiates several sub-controllers representing sub-modules of the application. Based upon user input (a click of a button or a link on a page), the controller decides that module to instantiate and display. It then tells the router to update the route to reflect the “destination” in the browser’s URL. In this respect, I’m using the router simply as a view, as all it does is update the URL, or on the reverse, tells the controller to display the right module or section depending upon what was entered in the URL.

In other words, a controller is responsible for controlling application flow. That’s all it does. It can listen and respond to events in the model and views. It can even trigger events to other controllers. But in no way should it contain data manipulation or view functionality. Leave that to the models and views of the system.

Look, MVC is not rocket science. But it may feel as if it is especially if you’re doing it wrong. And believe me, lots of people are doing it wrong.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s