Graham Lee has been writing a series of posts about Model View Controller and its meanings, with historical and contemporary contexts. His most recent post, Inside-Out Apps, is a good read.
The question is simple: What do we mean when we say MVC? The M and V, of course, are obvious. The Model represents your data and the actions you want to perform on it. The View displays that data. What’s in between is nebulous.
In iOS, Controllers are View Controllers, with the prefix UI
, suggesting that they’re very tightly coupled with the View layer. In Rails world, Controllers receive an “action” from the router and fetch and prepare data for the View, which is more of a template. In Java, Controllers explicitly observe their Models, updating their Views anytime something happens.
These are all very different ways of considering the C in MVC. They probably should all have different names. In fact, they do all have different names. And pretty pictures.
A quick note: My blog post is the first result for a Google search for “model view whatever”, which is a common catchphrase in the AngularJS world. The Angular team wants you to use “whatever” of the following systems work for you. Here is a link with more information from a member of the Angular team. Without further ado, here are the different patterns:
Model View Controller
True Model View Controller, as imagined in the Smalltalk world, is defined by three components that, crucially, each talk to both of the others. Models and Views with direct interaction! If someTableViewCell.object = object
makes you gag, then you should definitely not be calling your work “MVC”. Controllers here exist to capture user input, but not update the views and models (they do that using their own connection).
Model 2
Rails-land behaves a little bit differently. The basic architecture originates from Java-based servers and is called Model 2. Because all of the user inputs come in the form of page requests, those are routed through the Controller as “actions”, which massage the Models and send the data over to the Views. The Views render a response as output and return that to the browser.
Model View Adapter
In Objective-C, the pattern that we generally call MVC is more precisely referred to as Model View Adapter. In this system, Models and Views have no direct interaction, and actions (in the form of IBActions
, target-actions, KVO observation, delegate calls, and NSNotifications
) flow through the Adapter and update the relevant Models and Views.
Funneling all these different actions through a single object (the view controller) will result in a giant class, jokingly referred to as a Massive View Controller. It’s no wonder that the largest view controller in Brent Simmons’s Vesper codebase is 2900 lines long. You can tell a Ruby developer about a class with 3,000 lines of code, but not without fetching them a fresh pair of underwear first. Objective-C certainly is verbose, but verbosity can’t explain a god class.
I could talk about Massive View Controller for quite a while, so I will leave the discussion of that for another day/blogpost series/anthology.
MVVM
Model-View-ViewModel, hailed as a savior to Massive View Controller, does address some of the problems we face. Ash Furrow wrote a great writeup on the Teehan+Lax blog about MVVM. The View Controller is more formally bound to the View Layer (as it should be, since it’s really a View-level object). A new layer is inserted between the Model and the View couplet, called the View Model, and it is in charge of storing all of the presentation logic for a given view. Other texts might call this the Presenter pattern, which is a name I like, since it doesn’t include the name of both of the other components in the triad.
VIPER
The great thing about programming is that you can do whatever you want. Don’t be bound by ideology or mythology. If we can add a new layer between our View/View Controller combo and our Model, why can’t we add some more layers? VIPER — View-Interactor-Presenter-Entity-Router — attempts to do just that.
- The Model is renamed Entity, probably to make a contrived acronym
- The Router handles sending a requested “action” to the relevant Interactors
- Interactors (also sometimes called Service Objects) represent all business logic involving multiple Entities
- Presenters prepare data for display
- The View is now dumb as rocks
A small note: Interactors differ from classic Rails controllers in that they’re not bound to a Model (the way UsersController
might be tied to the User
model), but instead perform some task that involves one or more Entities. Controllers might still exist in this world to receive an action from the router and kick off the relevant Interactors or fetch data from the database.
The best part about adding more layers like this? Each layer, now with a specific role, is way more testable, and the View, usually the hardest layer to test, is now so dumb it doesn’t need unit testing.
So where does this leave us?
Primarily, it leaves us with a language for describing the patterns that we use on a day-to-day basis. What pattern is most appropriate for your app? That’s going to have to be for you to decide. Do you have a Model whose properties require a lot of transforming before display? Take that transformation code out of your View Controller, or — worse — your View, and place it in a Presenter object. A complex interaction between two Models might go in an Interactor. A table view that shows variable content might need a custom Data Source that knows how to manage all of its various states. An app that has content on each View Controller that could link to any other View Controller might need a Router. The possibilities are endless, and the only thing standing between you and your well-factored application is the New Class dialog.
Further reading and watching:
- Gang of Four: I’m still working my way through this text, but it has a lot of the prototypical versions of a lot of these patterns.
- Patterns of Enterprise Application Architecture: A more practical resource for this day and age, this one practically reads as a cookbook for a framework like Rails.
- Refactoring: A great way to think about how to improve your application’s architecture after it’s been built.
- collective/interactor: A simple Ruby Gem implementing the Interactor pattern.
- Architecture: The Lost Years: A talk from a Ruby conference in 2011, describing the similar issues that our Ruby-focused brothers and sisters are facing. 65 minutes.
I’ll leave you with Graham Lee:
When you get overly attached to MVC, then you look at every class you create and ask the question “is this a model, a view, or a controller?”. Because this question makes no sense, the answer doesn’t either: anything that isn’t evidently data or evidently graphics gets put into the amorphous “controller” collection, which eventually sucks your entire codebase into its innards like a black hole collapsing under its own weight.