January 26, 2021
Hot Topics:

Fat Models and Skinny Controllers Bring Out the Best in Your MVC Framework

  • By Jason Gilmore
  • Send Email »
  • More Articles »

In these waning moments of the 21st century's first decade, it seems an apropos time to look back at the events that shaped the world of web development over the course of the past 10 years. Although one could point to any number of developments, the emergence and widespread acceptance of MVC framework-based development seems to have played a particularly profound role in drastically improving the quality, security, and performance of the code used to power web sites both large and small. If you've been using one or several frameworks such as Rails, Django, or the Zend Framework, my guess is you'd be inclined to agree with this assessment.

Framework-based development is so advantageous because it relieves the developer of dozens of tedious decisions such as where to manage configuration data, how to access and manipulate database data, and what templating solution to employ. Employing the concept of convention over configuration, developers are able to instead devote the majority of their time and brain power to creating a powerful web application.

But a framework shouldn't be considered a panacea; it remains paramount for you to continue exploring and implementing best practices, which will further enhance the maintainability, reusability, and readability of your code. One such best practice involves adhering to a design decision that produces "fat models" and "skinny controllers." The term "fat" is derived from the idea of packing as much of the data-related logic into the model as possible while maintaining a streamlined, almost barren controller.

While it might seem an obvious strategy to restrict data-specific logic to the application's models, you'll often see a rather different approach employed. Let's take a look at this common yet misguided approach to see where things can go wrong. Although the concepts discussed throughout this tutorial apply to any MVC framework, we'll use the Zend Framework as the basis for the examples.

Going Forward in Reverse

All too often, developers tend to look at a Zend Framework model as little more than an obligatory file that must be created in order to establish the connection between the controller and underlying database. For instance, after configuring the database connection parameters within the application.ini file, all you need to do is create the following model to begin talking to the book table:

class Default_Model_Book extends Zend_Db_Table_Abstract
    protected $_name = 'book';
    protected $_primary = 'id';          

Despite the fact that you can extend the model with your own custom methods, it becomes all too easy to instead stuff your controller actions with model-specific logic. For instance, an action responsible for adding a new book to the book table might look like this (I'll simplify the validation procedures in order to keep this listing relatively short):

01 public function addAction()
02 {
03   if ($this->getRequest()->isPost()) {
05     $book = new Default_Model_Book();
07     // Define a message array
08     $this->view->messages = array();
10     // Title
11     if (! Zend_Validate::is($this->_request->getPost('title'), 'NotEmpty')) {
12       $this->view->messages[] = "Please provide a book title.";
13     }  
15     // Author
16     if (! Zend_Validate::is($this->_request->getPost('author'), 'NotEmpty')) {
17       $this->view->messages[] = "Please provide the author name.";
18     }  
20     if (count($this->view->messages) == 0) {
22       $data = array (
23         'title'  => $this->_request->getPost('title'),
24         'author' => $this->_request->getPost('author')   
25       );      
27       $book->insert($data);
29       $this->view->messages = "The book has been added.";
31     }
33   }
34 }

Let's review a few key lines in this listing:

  • Lines 11-18 validate the form field data provided by the user. In this case, we're just checking to ensure that neither form was blank. However, in a real-world situation you should perform additional verification tasks.
  • If line 20 determines that the $messages array is still blank (meaning no errors occurred during the validation process), lines 22-25 create the array used to insert the book data into the database. Line 27 completes the task by using the Zend_Db insert() method to insert the data.

While this action will suffice to complete the desired task, the approach has several drawbacks. Most notably, presuming you plan on also creating an action capable of modifying an existing book stored within the database, you'll need to create a second action that almost exactly reproduces the code found in the add() action:

public function editAction()
  if ($this->getRequest()->isPost()) {
    $book = new Default_Model_Book();
    // Define a message array
    $this->view->messages = array();
    // Title
    if (! Zend_Validate::is($this->_request->getPost('title'), 'NotEmpty')) {
      $this->view->messages[] = "Please provide a book title.";
    // Author
    if (! Zend_Validate::is($this->_request->getPost('author'), 'NotEmpty')) {
      $this->view->messages[] = "Please provide the author name.";
    if (count($this->view->messages) == 0) {
      $data = array (
        'title'  => $this->_request->getPost('title'),
        'author' => $this->_request->getPost('author')   
      $where = $book->getAdapter()->quoteInto('id = ?', $bookID);
      $book->update($data, $where);
      $this->view->messages = "The book has been modified.";

Because of this tedious redundancy, you'll need to be vigilant when changing your application logic to reflect changes made to the book model in order to ensure both the add() and edit() actions have been properly updated. However, by migrating the data-specific logic into the model, you can not only eliminate this unwanted redundancy, but also greatly reduce the total amount of code you'll need to maintain.

Page 1 of 2

This article was originally published on January 4, 2010

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date