Use of the Model View Controller (MVC) design pattern is generally accepted as a best practice in modern web applications. Like all design patterns, MVC is a reusable solution to a common problem. The MVC pattern is intended to address the following concerns:
- Support for multiple types of clients
- Reduce duplicate code when supporting multiple types of clients
- Isolating domain logic from the user interface
Note that items 2 and 3 are both dependent on item 1. Support for multiple types of clients is the single driving force behind the MVC design pattern. Following are some examples of client types:
- Web Browsers
- API Clients
- Admin Processes (e.g. CLI, cron, daemon)
- Unit Tests
- Native GUI
Realistically, how many of these different client types do you need to support? If you’re taking a RESTful approach, then Web Browsers and API Clients can be combined into one type of client, which we can just call User Agents. Likely you’re not building a Native GUI. This leaves you with the following three types of clients to support:
- User Agents
- Admin Processes
- Unit Tests
Do you really need to use the MVC design pattern in order to support User Agents, Admin Processes, and Unit Tests? Likely not. That’s not to say that there aren’t benefits to isolating domain logic from the user interface. However, if you don’t need to support multiple types of clients you should really question your use of the MVC design pattern. There are many approaches one can take to separating the domain logic and the user interface, of which MVC is just one.
This bring me to The MVC Paradox. Modern MVC web frameworks inadvertently encourage the coupling of domain logic and user interface. If you don’t need to support multiple types of clients, then decoupling the domain logic from the user interface is the only reason left to use the MVC design pattern.
Modern MVC web frameworks often involve a lot of boilerplate code just to support the primary client type of User Agents. This boilerplate code typically does little to help with supporting the other client types of Admin Processes and Unit Tests. As a result of the overhead introduced by this extra boilerplate code, developers often find themselves creating Fat Controllers (a side-effect of The MVC Paradox). Controllers take on too many responsibilities, both vertically and horizontally. Vertically, Controllers start to handle domain logic that should be pushed down to the Model layer. Horizontally, multiple concerns get stuffed into a handful of Controllers. These different horizontal concerns should be separated out into multiple Controllers (the Single Responsibility Principle). The overhead introduced by modern MVC web frameworks leads directly to these problems.
In contrast, take a look at the Slim PHP 5 micro framework. While not a completely RESTful framework, it does a pretty good job of addressing the concerns I’ve laid out in this post. Here is the “Hello world” example from Slim’s homepage:
<?php
require 'Slim/Slim.php';
$app = new Slim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name!";
});
$app->run();
?>
There is very little boilerplate code involved in handling a User Agent’s request. Minting and handling new routes is incredibly simple. Placing your entire front-end application layer in one file may seem absurd at first. However, it has the nice side-effect of making it painfully obvious if you start to handle domain logic outside of your Model layer or if one route is doing too many things. If your front-end application is large enough, then you can organize your routes into separate files.
This post is not intended to discourage you from using an MVC web framework. There are certainly times when such a framework is useful. As with any technology decision, carefully consider what problem you’re trying to solve and what technology will best address your problem set. Sometimes your technology choice can undermine the very purpose of using that technology in the first place, as is the case with The MVC Paradox.
Update (12/10/2012): This blog post has been translated into Serbo-Croatian by Jovana Milutinovich of Webhostinggeeks.com.