Tuesday, December 16, 2008

Symfony Design Patterns

Much has been said this days about how modern web frameworks interpret the MVC architectural pattern. I'm my case those articles made me rethink how I use a framework, specially Symfony.

This lead me to start a study on which patterns come into play while we develop a Symfony application. So while adding new features or refactoring existing ones I will know which class is in charge off doing the job.

Some of the patterns involved in Symfony are:
  • Front Controller
  • Command
  • Intercepting Filter
  • Context Object
  • Two Step View
  • Helper Object or View Helper
  • Table Data Gateway (i.e.: ArticlePeer.php)
  • Row Data Gateway (i.e.: Article.php)
  • Active Record
  • Single Table Inheritance
How do they interoperate? The Front Controller act as an entry point for our application. After it processes the request it will choose which Command should handle the request, in our case, one of our defined actions, that's why we have the execute prefix as a convention for the action
names. 

While the request is being processed Symfony filter chain will launch modifying the results accordingly. The idea of the Intercepting Filter pattern is to be possible to pre and post modify the request and the response without the need of change existing code. What's is a nice feature in Symfony is that we can add them directly on the filters.yml file making really convenient to use. Also we can specify filters that only apply for certain modules, making the process really flexible.

On the View side Symfony uses the Two Step View, so generally the result of our request will be decorated with a common layout to add consistency to our website. Here we can also use helpers, as they are explained in the Symfony documentation or add Helper classes. The Helpers classes can retrieve data from the Model, format data, or anything our application needs to work on the View side. With them we make our templates clear and free off script code, so it's easy to non developers to work on them.

Also Symfony allows to use specific view classes by module, adding extra flexibility in the way we can handle the response, let's say, to return JSON data.

As a way to keep a reference to the context, Symfony implement the -some guess please- Context Object pattern, which I won't talk about here.

Then we have the DAL, which in my case is powered by Propel. This ORM implements the Table Data Gateway and the Row Data Gateway patterns. As they name implies they act as gateways to our tables and rows, but because generally we add some domain logic inside them they start to act as Active Records.

A cool feature of Propel is that it implements the Single Table Inheritance pattern. This pattern is useful when we have a table for publications where we store Magazines and Books. If our table has a field acting as the type of the row, and in our application we treat them differently, whether if they are books or magazines, then this pattern let us work with Publication, Book and Magazine classes.

After this research I came with the following conclusion:
  • Use the controllers to handle user input data and choose which view to display.
  • If there, remove all business logic from the controllers.
  • Let the Model do it's job with the provided data. 
  • When handling data to the model, do it as an array of normalized data -or proper objects pertinent to our application-, the model shouldn't know about the request.
  • Let helpers or Helper Objects handle the data formating, avoiding script code inside templates.
  • Use Propel as a DAL and refactor towards a Model. This means to remove complex business logic from the Propel classes.
What helped my during this research where the following books, which I highly recommend.

5 comments:

Ziad said...

Hi Alvaro,

Just wanted to say thanks for the post, very useful, specially the conclusion. I've been re-thinking about how I should be structuring my apps with a lot of talk going on regarding what should go into controllers/models and MVC in general.

Alvaro Videla said...

@Zaid Thanks for the comment. I think there is much to say about this topic, maybe in a future post I wll go a little bit deep in the subject, because I think it's essential to learn to develop good web applications.

Ziad said...

I agree 100%. The PHP community has got far too much criticism for being cowboy web developers and its time to change that view. Having said that I do think we have to be careful to not go too far (http://phpadvent.org/2008/php-is-not-java-by-luke-welling), use PHP to its strength using good software engineering principles.

BTW I was just wondering, you said that you wanted to use Propel as a DAL (or DAO?) and move the business logic away to models. What are 'models' in Symfony? Aren't models and Propel objects the one and the same in Symfony? It seems most frameworks take this approach which makes it difficult to keep your business logic independent of the data access. I think frameworks need to incorporate this concept of framework independent models into the framework to allow for better designed software.

Alvaro Videla said...

About your DAL and models question. I think that symfony uses the word model indistinctly, but for some cases it's needed to have separate business objects. In a project I work for, we have Image handlers classes which take care of moving the upload images, and handle all the data related to the images. I think there are more examples like this one that shows that not everything should go inside Propel.

Ziad said...

OK I think this is probably like CakePHP's components.