When it comes to design, whenever I hear that something “should” be done a specific way, I feel compelled to ask a simple question: “Why?” Indeed, if everyone believed and practiced design patterns without understanding them, engineering would be yet another religion. Therefore it is sometimes worthwhile to revisit, and challenge, certain design conventions.
To that end, what about the “Fat model, skinny controller” (and vice versa) approach in the MVC pattern? Where should the controller’s charges end and the model’s begin?
Well, the main goals of every design pattern are encapsulation, isolation and code reusability. The MVC pattern consists of three parts: the model, responsible for business rules and data management; the view – for representing the model in the user interface; and the controller, which binds the model and view i.e. makes them work together. This is of course obvious, but the key point to keep in mind is that the model knows nothing about either the controller or the view.
For example, imagine building a typical web application. You have carefully implemented HTTP request handling, HTML generation, user session handling, and other web-specific tasks, and all is working perfectly. But then your client says, “This works great, but we need to be able to interact with the application using the RPC protocol. Can that be implemented?”
Would you have to change the code of your models? Would there be unusable code, i.e. code that has no use outside of a web implementation? If the answer is “yes,” you should change your design approach. The model should be reusable without modification in any interface or implementation: web, RPC, shell, desktop, mobile, you name it. Therefore, any interface-specific code should be kept in the controller (or view).
Incidentally, test-driven development is a good way to keep a model clean. If you test models in isolation, there will be no unnecessary code, because the test environment is yet another interface!
There is a bit of nuance, however, in the case of data validation. At first blush, this would clearly be the model’s job. However, this does introduce a huge performance overhead in the case of batch processing of data from a trusted source. For this reason, I think the validation layer should be separate from the model one, or should be switchable. And of course it must not be mixed with the sanitization layer.
For example, protecting from an XSS attack is the web controller’s job, while protecting from SQL injection is the model’s. Therefore, a validation layer, if it is needed, should be placed somewhere between these two. The solution to this problem is dependent on the task, there is unfortunately no silver bullet.
In summary: keep models clean and reusable. Whether they are “fat” or “skinny” does not matter, as long as they are in the shape they need to do the job.
Originally posted on Dmitry Vakhrushev’s personal blog at www.kr41.net.