If there’s one thing about MVC, it’s that no two implementations completely agree on how the controller should work. MVC implementors seem to all intuitively understand the concept of a model and a view, and why it’s a good idea to keep them separate, but confusion descends when they get to the controller. Observers of this phenomenon might be tempted to rename the pattern Model/View/Confusion!
Martin Fowler notes the same problem himself, and says “a lot of the reason for this is that parts of classic MVC don’t really make sense for rich clients these days” and that “MVC assumes we are manipulating regular Smalltalk objects”. There is clearly some truth to this; web developers trying to implement MVC will have no other option but to have their controller listen to the view, since that’s how HTML works, whereas in classic MVC the view is a write-only canvas, and it’s only the controller that’s informed about user gestures. So, to some extent, we’ve all been using a Smalltalk pattern that doesn’t quite work in any other language.
Having switched over to the MVVM pattern in the last year or so, I’ve now come to a somewhat different conclusion. When you look at a typical MVC implementation you notice a common theme. You tend to end up with a perfectly formed model and view, and all the crud left over gets stuffed into the controller. Just like the film Twins really, but this version is called Triplets: Model/View/Crapola.
The most obvious problem with the typical non-SmallTalk controller is that it doesn’t adhere to the single-responsibility principle, being both responsible for registering user gesture listeners on the view, and acting upon them.
But there’s a more subtle problem too, one that becomes more obvious once you’ve switched to MVVM; the controller essentially acts as an informal view-to-model adaptor, adapting the views actions onto the underlying models, and this becomes messier the more models there are, and the less those models were custom designed to work with the view. In fact, as the mismatch gets bigger, even the view stops looking so shiny, as it also has to act as an informal view-to-model adaptor in the opposite direction, from the model(s) to the view.
MVC promotes code cleanliness to a point, but as it begins to break down it also unintentionally discourages re-use; the controller and view becoming more and more borked as you try to create views over legacy models they weren’t designed to work with! Informal view-model-adaptors only get you so far, and the MVVM pattern neatly solves this problem by introducing the view-model, a model of the view that sits between the view and underlying logical models.
The view-model is essentially a formal bi-directional view-to-model adaptor, getting rid of the need to have this code laced through the controller and the view. Once you’ve made this change your view will become truly Schwarzenegger like in design, and the controller will disappear in a puff of intellectual smoke, it’s final responsibility of view-to-model binding now being performed generically by the MVVM library of your choice! Yes, amigos míos, all hail our new MVVM overlords!
When we switched to MVVM at Caplin we weren’t trying to escape any fundamental problems with MVC; we had much bigger fish to fry. One of our strongest motivations was trying to escape the testing crisis we found ourselves within at the time. The automated tests we had back then, running on a powerful bank of servers, took all night to run and were unreliable to boot, having to be manually analyzed by a QA each morning. Our replacement tests on the other hand are highly reliable, and can be run on a single developer machine in about 10 minutes!
Only two fundamental changes were required to get us there:
- We now test our components in isolation rather than testing via the main app, using test-by-contract to cover any external interactions.
- We no longer test via the GUI since it’s really slow, and perform all our tests via the view-model, which is ultimately the same thing anyway.