This is article three of my Introduction to Rails series. You can find the previous article under the title Zero Downtime Deployment with Capistrano.
The subjects of today’s post are not specific in any way to Ruby on Rails. I encountered them first when I started developing on the Magento platform, though I’d heard a joking quote about MVC (Model-View-Controller) before: “MVC is an architecture they ask you to explain on job interviews but which nobody really understands.” Silly me, I took that to mean it wasn’t worth learning.
Actually, MVC isn’t difficult to understand. It’s a way of organizing your program’s code to help ensure separation of concerns. The easiest way to explain is with an example blog application.
In traditional PHP land this starts at the top of the script and works down, including various other files as it goes. In a MVC system all requests started by going through the front end routing code. This is done by matching the URL against a list of route patterns, which in Rails is found in the ‘routes.rb’ file. These patterns are linked to ‘controllers’, which are just classes, and ‘actions’, which are just class methods. The router calls the action method which then takes over execution of the application.
What does that mean in our example? Instead of going to “/blog.php” which calls the script directly, any request (such as “/blog” or “/blog.html”) first goes to the front end router which examines the given address and calls the “index” method of your “BlogController” class.
Models are classes/objects which have direct access to the application data. In our example of a blog app we might have a model for the entire Blog (containing configuration data, title, etc), one for each BlogPost (poster, subject, content, tags, meta information, etc), one for each Comment (poster, content), and one for each User (username, e-mail address, password, etc). Each of these models is responsible for reading and manipulating the data it represents.
By encapsulating each data structure within a model we can ensure the integrity of the system. Do you need every username to be greater than 6 characters in length? Add a restriction to the User model. Perhaps you want a new BlogPost to be created every time a new User registers (it’s a stretch, I know, but stay with me). Add code to your User model to create a blog post every time a new user is saved.
These model restrictions are often what people refer to as “business logic”. Your business requires that interactions with the data behave in certain ways. The model layer is coded to ensure these requirements are met.
Views are the easiest part of the system to understand. Views are simply templates with tagged positions where data may be entered. For example, you might have a view which represents a blog post. This view will have areas for the title, the content, the author, the date and so on. You might have another view for a user profile with spots for username, first name, last name, and e-mail address. What’s important, however, is that views are simple templates completely devoid of application data. This data will be filled in as needed by the controller.
Controllers are the one and only part of the code which can take action in response to external input. I’d like to repeat this because it’s very important. No other area besides your controller has any awareness of people actually using the system. This restriction can be frustrating at times, but it is part of the underlying principle of separation of concerns. It is not without reason, either. By handling all input in the controllers it becomes the only place where authorization and authentication checks are required.
Models and views are followers; they do only what the controller tells them to do. The controller initiates all the action; however, it lacks the ability to do anything directly. The controller will never output information to the user. It will never add a new blog post or search keywords. It can, however, tell the view to output information to the user. It can tell the model to add a new blog post or search keywords.
In our example application, the request hitting our BlogController’s “index” action will be set up as follows. First, the controller tells the BlogPost class to load up the first 5 blog posts into an array. Then the controller sends this array of BlogPosts to the “blog/index” view which displays 5 copies of the “blog/post” view each embedded with data from one BlogPost.
When I first encountered MVC I thought “this is needlessly complex”. Actually, it was Magento that was needlessly complex, but while MVC can be a bit of a beast the truth is at the other end of the scale. MVC makes those complex programs simpler! After you’ve passed the initial phase of being lost trying to follow the mysterious and sometimes (in the case of Rails) transparent flow of code execution between the router and the MVC components you’ll reach a point where it is quicker and easier to use MVC than to follow a top-to-bottom PHP script.
That’s it for routing and MVC. If you want more information I suggest reading Design Patterns: Elements of Reusable Object-Oriented Software. Actually, if you plan to get serious about programming I suggest you read it whether or not you want to hear more about MVC :) Stay tuned for the next part in this Introduction to Ruby on Rails series, coming next Monday (January 28th). [Update: Automated Testing has been posted.]