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.
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.
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.”
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.