Chapter 3. The Model

3.1. Introduction

In Chapter 2, we introduced the Model-View-Controller (MVC) design pattern and explored how it is composed, and why it is beneficial to web applications. I noted back then that the most difficult concept to grasp is that of the Model.

There are numerous interpretations of the Model but for many programmers the Model is equated with data access, a misconception most frameworks inadvertently promote by not obviously acknowledging that they do not provide full Models. In our buzzword inundated community, many frameworks leave the definition of the Model unclear and obscured in their documentation.

In reality, a Model is a representation of one part of the Domain Model. The Domain Model is comprised of many connected Model objects which represent a system's data and behaviour. It's completely independent of the Controller and View.

Talking about Domain Models and systems isn't an obvious revelation, so consider the analogy of a supercomputer running a simulation for climate modelling. While the simulation might be running on any number of backend architectures, its defining features include the data that create the initial conditions of the climate model and the logic rules which determine the laws governing the model's evolution over time. Within this overall system are many interconnected and interdependent models. The M in MVC is called the Model for similar reasons - it models a system's behaviour as much as defining it's data.

The mapping of a Model to a database or any other storage medium where its data is recorded can be a simple mapping where each Model relates to a single database table or, where a Model is composed of many child Models, a more complex affair involving multiple tables. However, the mapping of Models to a database or other storage medium does not define the Model. In fact you can add data access to developed Models after the fact and this is not an uncommon approach! The Model includes the business rules, behaviour and constraints of the data it represents, and these may be stored to a database (generally once they are reduced to quantifiable configurable rules) after they have been designed and documented.

The other side of the Model issue, is how they are utilised and how we balance Models against the other components of MVC. The primary rule, of course, is that Models must be unaware of how they are presented. Presentation is the responsibility of both the View and the Controller who must be aware of the Models they are interacting with. It's a one way street though. Controllers and Views may know the Model, but the Model will never know about them.

3.2. Clarifying The Model

As I explained in Chapter 2, the Model in Model-View-Controller has two primary roles: to maintain state between requests by storing all application data before the request ends so it can be retrieved for subsequent requests, and also to host the business rules and constraints which apply to the data our state is composed of. However, no web application framework can predict business rules, forms of data or constraints, and so frameworks fall short of the mark of being capable of offering a complete Model. In effect, developers must create all Models themselves since they are specific to the application. This doesn't mean a framework's Model related features are useless! Frameworks still offer invaluable components to assist in accessing Model's data from databases, web services, and other sources.

This should, however, highlight an essential concept to bear in mind: a Model is not merely accessing a database. It encapsulates all the data and behaviour of a specific unit within the application, excluding any presentation related logic.

This is all fine in theory, so how about a quick example? Our example follows a simple Model design, where each Model maps to a single database table. Additionally, our Model is represented as a subclass of Zend_Db_Table_Abstract. This forms an is-a relationship with the data access layer, rather than the alternative has-a relationship common in more complex Domain Models where data access is added to Models, and not necessarily used as the foundation upon which Models are built.

  • <?php
  • class Order extends Zend_Db_Table_Abstract
  • {
  • protected $_name = 'orders';
  • protected $_limit = 200;
  • protected $_authorised = false;
  • public function setLimit($limit)
  • {
  • $this->_limit = $limit;
  • }
  • public function setAuthorised($auth)
  • {
  • $this->_authorised = (bool) $auth;
  • }
  • public function insert(array $data)
  • {
  • if ($data['amount'] > $this->_limit
  • && $this->_authorised === false) {
  • throw new Exception('Unauthorised transaction of greater than '
  • . $this->_limit . ' units');
  • }
  • return parent::insert($data);
  • }
  • }

In the class above, we've implemented a really simple Order Model which relies on users having sufficient authority before allowing Orders with amounts greater than 200. If such authority is not granted by some external force, an Exception is thrown to be picked up by a Controller which can handle the problem. The limit is 200 by default but can be modified from the outside (e.g. a configuration file).

Another useful way of considering the idea of a Model is to strip out the business logic in the above class and think about where it should go if not the Model. This leads us to another classic concept in thinking which we'll examine.

3.3. In Programming, Fat Models Are Preferable To Size Zero Models

Jamis Buck (the author of Capistrano now employed by 37signals) once wrote about a concept called "Skinny Controller, Fat Model" (our own Chris Hartjes wrote a blog entry on the same topic). I've always liked the simplicity of this concept since it illustrates a key facet of MVC. In this concept, it is considered best practice to maintain application logic (like our business logic from above) within the Model as much as possible and not in either of the View or Controller layers.

The View should only be concerned with generating and presenting a UI so users can receive requested output and communicate requests to the application. Controllers are the orchestrators who translate UI inputs into actions on the Model and pass back output to the client from whatever View has been made aware of the Models it is to present. Controllers must define application behaviour only in the sense that they map input from the UI onto calls in Models and handle client interaction, but beyond that role it should be clear all other application logic is contained within the Model. Controllers are lowly creatures with minimal code who just set the stage and let things work in an organised fashion for the environment the application operates in.

This creates an important link in that both the Controller and the View are geared towards presentation. This is obvious with the View but not so much with the Controller. However, since the Controller is so preoccupied with handling the HTTP or Console mechanics of the application, it really is lodged in the presentation layer.

PHP developers, by and large however, don't fully understand these ideas in depth. Many see the Model as a fancy term for database access and others equate it with various design patterns for database access like Active Record, Data Mapper and Table Data Gateway. We can't put it all on PHP developers however - there are plenty of Ruby and Python developers with similar notions. This is the misconception frameworks often promote unintentionally by not defining the Model up front. By not fully comprehending what a Model is, why it's such a great idea, and how one should be developed and deployed, developers unintentionally shoot themselves in the foot by adopting other alternatives.

On this alternative road, developers restrict their Models to accessing data and push business logic into the Controller. This is a problem because it mixes business logic into that part of the MVC which is concerned with presentation. Yes, again, Controllers are part of the presentation layer. Mapping UI inputs to Model actions, locating and rendering Views, handling client communications... See, presentation!

The simplest means of demonstrating this problem is assessing the ability to unit test such business logic. Controllers are notorious for being difficult to test since they are presentational units which, like the View, require entire application runs for each discrete test. Then you can only test the final output, meaning that Controller tests make assertions against the data passed to a View, or even just the content of the final View itself. If you are familiar with unit testing, this breaches the basic principle of testing units in isolation, and it also makes the practices of Test-Driven Design (TDD) and Behavioural-Driven Development (BDD) difficult to apply to business rules. Most of these unit tests are really functional tests, testing the entire application and not just one focused part of it.

Another practical demonstration of this problem is imagining a new project you just completed using the Zend Framework. The client is impressed but breaks the news that they need all their applications transitioned to Symfony. Granted, not a common scenario, but more than one application has needed to migrate to another platform in the past or required a last ditch rennovation.

If you pushed all business rules into the Domain Model, then you can pretty much pick up and migrate with ease. Models are essentially independent of the web application framework, although they may rely on data access components that are not so easily migrated (not an issue for the Zend Framework since it's loosely coupled) unless a has-a relationship to the data access layer was employed to allow data access components to be more easily replaced with substitutes. If you pushed business logic into Controllers, you'll realise your mistake - Symfony cannot execute Zend Framework Controllers! Now you need to start migrating entire tracts of source code between frameworks, rather than the minimal orchestral logic a Controller should restrict itself to. Consider this in light of the unit testing difficulties and you can also wave goodbye to those functional tests relying on Zend_Test.

At the end of the day, Models are discrete classes that can be tested in isolation from the presentation layer and migrated to other framework systems with ease. Controllers are the exact opposite! They are bound to the presentation layer and the framework itself to such an extent that they are not instantly portable.

3.4. The Fat Stupid Ugly Controller

In the past, PHP developers have employed two common design patterns to create applications: the Page Controller and Transaction Script. Despite their lofty names and standing as Design Patterns, they merely refer to the most intuitive approach of creating content "pages" with PHP where each page is represented by a PHP script containing all the operations for that page laid out in a procedural style. Although we've moved on from these patterns towards MVC, old habits die hard and the mentality used for these has found a way to be reborn within the confines of the Model-View-Controller.

Since it's catchy, colourful, and might win me recognition for my wit (or lack thereof), I refer to these reborn elements as Fat Stupid Ugly Controllers (FSUCs). Initially I had considered Stupid Fat Ugly Controllers (SFUC) but it seemed a bit too rude even if it's more emphatically denigrating.

The typical FSUC is the product of remaining in the Page Controller and Transaction Script mental mode when adopting MVC, and continually demoting Models by restricting them to data access roles only. It generates Controllers which break the mold of the "Skinny Controller, Fat Model" paradigm and reverses it completely. Now, business logic is pushed entirely into Controllers.

The typical FSUC performs all business logic operations, and struggles with the concept of a Domain Model since Controllers are not discrete reusable classes. Developers use them as naturally, and as frequently, as they ever did Page Controllers with much the same impact. Now every page in the application is bound to a horrid mess of spaghetti code which shows few benefits of good Object Oriented Design. Controller methods expand, helper classes explode in number as a retrofit towards the Domain Model is attempted at some level, and unit testing is often abandoned or restricted to functional testing since you need to initialise the whole MVC operation to do anything - which is hard when there's no set View because your web designer hasn't been hired yet.

FSUCs are big, unwieldy, ugly looking and fat. The quasi programming term that applies is "bloated". They perform roles they were never intended for. They are a complete reversal of everything you are taught to do in applying Object Oriented Programming principles. Developers, for some mysterious reason, seem to prefer FSUCs to Models despite the fact those Controllers really are just Page Controllers or Transaction Scripts in disguise.

Remember the problems from replacing Models with Controllers I discussed? Unit testing becomes difficult, applying TDD or BDD becomes near impossible without an obsessive stubborn streak, and you'll never be able to efficiently migrate this mass to another framework without redeveloping half the application.

This type of tight coupling and confused code design is the kind of thing Kent Beck fans are supposed to identify as a "code smell" in Refactoring. But it's not the only smelly artifact of FSUCs. FSUCs have frequently duplicated code, lack refactored extracted classes and they can be a nightmare to maintain. In short, they are borderline evil in anything but the simplest of prototyped applications.

Consider this example of a simple Model and FSUC combination:

  • <?php
  • class Order extends Zend_Db_Table_Abstract
  • {
  • protected $_name = 'orders';
  • }
  • class OrderController extends Zend_Controller_Action
  • {
  • protected $_limit = 200;
  • protected $_authorised = false;
  • public function init()
  • {
  • // magically set authorised flag or limit
  • // restriction here somehow
  • }
  • public function createAction()
  • {
  • // keeping it simple, if insecure
  • $data = $_POST;
  • $model = new Order;
  • if ($data['amount'] > $this->_limit
  • && $this->_authorised === false) {
  • // setup View to report the error
  • }
  • $model->insert($data);
  • }
  • }

Now imagine you want to implement a new feature called Order Batch where multiple Orders are processed and committed in batches. You immediately meet a problem - all the Order business logic is stuck in OrderController::createAction() which is not a reusable object. Will you copy the business rules to the new BatchController? Create a task queue to make multiple calls to the OrderController? Perhaps you can create a new Action Helper common to all Controllers? Keep ticking off the options until you realise a standalone class (i.e. Model) works best. It's the most flexible solution, and it obeys the concept of Keep It Simple Stupid (KISS).

3.5. Controllers Are Not The Data Police

The other facet of this lack of Model consideration is that when developers are convinced that Models should be minimal, and Controllers are all important, it reinforces a reliance on Controllers to take on a new role as the Data Police (a primary reason why they mutate into FSUCs).

In this role, all requests for data are channeled through a Controller. However this is not always a suitable strategy, especially when it's used in scenarios where other parts of the application only want to read data.

Here's an example I came across on the Zend Framework mailing lists while writing the book. In some unknown application, every page displays the total number of users somewhere in a sidebar. In order to implant this piece of data into a template, all Controller Actions across the application are retrieving the data and passing it to the current View. Much of the source code to accomplish this is copied into every Controller's initialisation method.

The obvious problem with the above is code duplication. With the source code copied to every Controller class, it's going to be difficult to maintain. There's a more nefarious element however. From the description, the only part of the application using this data is the View. The Controller never needs it directly, never manipulates it, and never performs any writes back to the Model for it. Given that the Controller never uses this data, why is it the component doing all the legwork to retrieve it?

The solution I suggested to the mailing list was to eradicate the middleman. Since the Controller never uses the data, step back a bit and let the View retrieve the data itself using a simple View Helper (one class, infinitely reusable). This little story illustrates a common fallacy - Controllers are not the sole source of all Model interaction. They do not exclusively police data. Views can themselves pull up a Model without the Controller's permission and retrieve data for presentation more efficiently.

If you expand this option further, you'll quickly realise this is one more layer of complexity you could offload from Controllers, reducing their average size even further. The thinner the better!

It's worth noting that the term View Helper, is associated with Java as a design pattern in the book "Core J2EE Design Patterns". In MVC, there is every indication that Views should know about the Models they are presenting. Not just whatever arrays we decide to spoon feed them from Controllers.

3.6. Conclusion

Reading through this chapter will have hopefully inspired some new ideas. The one idea which underpins everything else above is applying elegant Object Oriented Programming. Bloated Controllers which are not decoupled from the underlying framework pose obvious problems in retrospect whereas Models, which are heavily decoupled and structured as a system of independent classes, are far more maintainable, testable and portable. This decoupling of Models makes them easy to access from anywhere, including the View.

This chapter concludes our examination of MVC, and I trust the diversion into more detail concerning the Model in this pattern was time well spent. In Chapter 4 which follows, we'll spend some time examining the Zend Framework in light of MVC and meet some of its core components for implementing MVC architected applications.

Powered by jQuery Powered by Zend Framework Powered by Nginx Http Server