MVC is an architectural design pattern that encourages improved application organization through a separation of concerns. It enforces the isolation of business data (Models) from user interfaces (Views), with a third component (Controllers) traditionally managing logic and user-input. The pattern was originally designed by Trygve Reenskaug during his time working on Smalltalk-80 (1979) where it was initially called Model-View-Controller-Editor. MVC went on to be described in depth in 1995’s “Design Patterns: Elements of Reusable Object-Oriented Software” (The “GoF” book), which played a role in popularizing its use.
It’s important to understand what the original MVC pattern was aiming to solve as it’s mutated quite heavily since the days of its origin. Back in the 70’s, graphical user-interfaces were few and far between and a concept known as Separated Presentation began to be used as a means to make a clear division between domain objects which modeled concepts in the real world (e.g a photo, a person) and the presentation objects which were rendered to the user’s screen.
The Smalltalk-80 implementation of MVC took this concept further and had an objective of separating out the application logic from the user interface. The idea was that decoupling these parts of the application would also allow the reuse of models for other interfaces in the application. There are some interesting points worth noting about Smalltalk-80’s MVC architecture:
A Model represented domain-specific data and was ignorant of the user-interface (Views and Controllers). When a model changed, it would inform its observers.
A View represented the current state of a Model. The Observer pattern was used for letting the View know whenever the Model was updated or modified.
Presentation was taken care of by the View, but there wasn’t just a single View and Controller – a View-Controller pair was required for each section or element being displayed on the screen.
The Controllers role in this pair was handling user interaction (such as key-presses and actions e.g. clicks), making decisions for the View.
Developers are sometimes surprised when they learn that the Observer pattern (nowadays commonly implemented as the Publish/Subscribe variation) was included as a part of MVC’s architecture many decades ago. In Smalltalk-80’s MVC, the View observes the Model. As mentioned in the bullet point above, anytime the Model changes, the Views react. A simple example of this is an application backed by stock market data – in order for the application to be useful, any change to the data in our Models should result in the View being refreshed instantly.
Martin Fowler has done an excellent job of writing about the origins of MVC over the years and if interested in some further historical information about Smalltalk-80’s MVC, I recommend reading his work.
We know that MVC is composed of three core components:
Models manage the data for an application. They are concerned with neither the user-interface nor presentation layers but instead represent unique forms of data that an application may require. When a model changes (e.g when it is updated), it will typically notify its observers (e.g views, a concept we will cover shortly) that a change has occurred so that they may react accordingly.
The built-in capabilities of models vary across frameworks, however it is quite common for them to support validation of attributes, where attributes represent the properties of the model, such as a model identifier. When using models in real-world applications we generally also desire model persistence. Persistence allows us to edit and update models with the knowledge that its most recent state will be saved in either: memory, in a user’s localStorage data-store or synchronized with a database.
In addition, a model may also have multiple views observing it. If say, our photo model contained meta-data such as its location (longitude and latitude), friends that were present in the photo (a list of identifiers) and a list of tags, a developer may decide to provide a single view to display each of these three facets.
It is not uncommon for modern MVC/MV* frameworks to provide a means to group models together (e.g. in Backbone, these groups are referred to as “collections”). Managing models in groups allows us to write application logic based on notifications from the group should any model it contains be changed. This avoids the need to manually observe individual model instances.
A sample grouping of models into a simplified Backbone collection can be seen below.
So to summarize, models are primarily concerned with business data.
A view typically observes a model and is notified when the model changes, allowing the view to update itself accordingly. Design pattern literature commonly refers to views as “dumb” given that their knowledge of models and controllers in an application is limited.
Users are able to interact with views and this includes the ability to read and edit (i.e get or set the attribute values in) models. As the view is the presentation layer, we generally present the ability to edit and update in a user-friendly fashion. For example, in the former photo gallery application we discussed earlier, model editing could be facilitated through an “edit’ view where a user who has selected a specific photo could edit its meta-data.
The actual task of updating the model falls to controllers (which we will be covering shortly).
The photoModel then adds our render() callback as one of its subscribers so that through the Observer pattern we can trigger the view to update when the model changes.
One may wonder where user-interaction comes into play here. When users click on any elements within the view, it’s not the view’s responsibility to know what to do next. It relies on a controller to make this decision for it. In our sample implementation, this is achieved by adding an event listener to photoElwhich will delegate handling the click behavior back to the controller, passing the model information along with it in case it’s needed.
The benefit of this architecture is that each component plays its own separate role in making the application function as needed.
It has long been considered (and proven) a performance bad practice to manually create large blocks of HTML markup in-memory through string concatenation. Developers doing so have fallen prey to inperformantly iterating through their data, wrapping it in nested divs and using outdated techniques such as document.write to inject the “template” into the DOM. As this typically means keeping scripted markup inline with our standard markup, it can quickly become both difficult to read and more importantly, maintain such disasters, especially when building non-trivially sized applications.
Below we can see two examples of HTML templates. One implemented using the popular Handlebars.js framework and another using Underscore’s templates.
Note that templates are not themselves views. Developers coming from a Struts Model 2 architecture may feel like a template *is* a view, but it isn’t. A view is an object which observes a model and keeps the visual representation up-to-date. A template *might* be a declarative way to specify part or even all of a view object so that it can be generated from the template specification.
The role of navigation thus falls to a “router”, which assists in managing application state (e.g allowing users to bookmark a particular view they have navigated to). As routers are, however, neither a part of MVC nor present in every MVC-like framework, I will not be going into them in greater detail in this section.
To summarize, views are a visual representation of our application data.
Controllers are an intermediary between models and views which are classically responsible for updating the model when the user manipulates the view.
In our photo gallery application, a controller would be responsible for handling changes the user made to the edit view for a particular photo, updating a specific photo model when a user has finished editing.
Remember that the controllers fulfill one role in MVC: the facilitation of the Strategy pattern for the view. In the Strategy pattern regard, the view delegates to the controller at the view’s discretion. So, that’s how the strategy pattern works. The view could delegate handling user events to the controller when the view sees fit. The view *could* delegate handling model change events to the controller if the view sees fit, but this is not the traditional role of the controller.
As an example, let’s briefly review the architecture of the popular architectural framework Backbone.js. Backbone contains models and views (somewhat similar to what we reviewed earlier), however it doesn’t actually have true controllers. Its views and routers act a little similar to a controller, but neither are actually controllers on their own.
In this respect, contrary to what might be mentioned in the official documentation or in blog posts, Backbone is neither a truly MVC/MVP nor MVVM framework. It’s in fact better to consider it a member of the MV* family which approaches architecture in its own way. There is of course nothing wrong with this, but it is important to distinguish between classical MVC and MV* should we begin relying on advice from classical literature on the former to help with the latter.
Controllers in another library (Spine.js) vs Backbone.js
It can thus be useful for us to review the controller from another MVC framework to appreciate the difference in implementations and further demonstrate how nontraditionally frameworks approach the role of the controller. For this, let’s take a look at a sample controller from Spine.js:
In this example, we’re going to have a controller called PhotosController which will be in charge of individual photos in the application. It will ensure that when the view updates (e.g a user edited the photo meta-data) the corresponding model does too.
Note: We won’t be delving heavily into Spine.js at all, but will just take a ten-foot view of what its controllers can do:
In Spine, controllers are considered the glue for an application, adding and responding to DOM events, rendering templates and ensuring that views and models are kept in sync (which makes sense in the context of what we know to be a controller).
What this provides us with is a very lightweight, simple way to manage changes between the model and the view.
Later on in this section we’re going to revisit the differences between Backbone and traditional MVC, but for now let’s focus on controllers.
In Backbone, one shares the responsibility of a controller with both the Backbone.View and Backbone.Router. Some time ago Backbone did once come with its own Backbone.Controller, but as the naming for this component didn’t make sense for the context in which it was being used, it was later renamed to Router.
Routers handle a little more of the controller responsibility as it’s possible to bind the events there for models and have our view respond to DOM events and rendering. As Tim Branyen (another Bocoup-based Backbone contributor) has also previously pointed out, it’s possible to get away with not needing Backbone.Router at all for this, so a way to think about it using the Router paradigm is probably:
To summarize, the takeaway from this section is that controllers manage the logic and coordination between models and views in an application.
What does MVC give us?
This separation of concerns in MVC facilitates simpler modularization of an application’s functionality and enables:
Easier overall maintenance. When updates need to be made to the application it is very clear whether the changes are data-centric, meaning changes to models and possibly controllers, or merely visual, meaning changes to views.
Decoupling models and views means that it is significantly more straight-forward to write unit tests for business logic
Duplication of low-level model and controller code (i.e what we may have been using instead) is eliminated across the application
Depending on the size of the application and separation of roles, this modularity allows developers responsible for core logic and developers working on the user-interfaces to work simultaneously
At this point in the book, we should have a basic understanding of what the MVC pattern provides, but there’s still some fascinating information about it worth noting.
The GoF do not refer to MVC as a design pattern, but rather consider it a set of classes to build a user interface. In their view, it’s actually a variation of three classical design patterns: the Observer, Strategy and Composite patterns. Depending on how MVC has been implemented in a framework, it may also use the Factory and Template patterns. The GoF book mentions these patterns as useful extras when working with MVC.
As we have discussed, models represent application data whilst views are what the user is presented on screen. As such, MVC relies on the Observer pattern for some of its core communication (something that surprisingly isn’t covered in many articles about the MVC pattern). When a model is changed it notifies its observers (Views) that something has been updated – this is perhaps the most important relationship in MVC. The observer nature of this relationship is also what facilitates multiple views being attached to the same model.
For developers interested in knowing more about the decoupled nature of MVC (once again, depending on the implementation), one of the goals of the pattern is to help define one-to-many relationships between a topic (data object) and its observers. When a topic changes, its observers are updated. Views and controllers have a slightly different relationship. Controllers facilitate views to respond to different user input and are an example of the Strategy pattern.