The last decade has been witness to the second iteration of web design and development. Web sites have transformed into web applications and rarely are new projects commissioned that do not involve some element of interactivity. The increasing complexity of the software being developed for the internet fuelled a requirement for structured and considered application design.
Today the most common design pattern for web software is the Model-View-Controller (MVC) pattern. The widespread adoption of the MVC design pattern was supported, in part, by the success and popularity of the Ruby on Rails framework. MVC is now synonymous with web application development across all platforms.
With the rising complexity of projects developed for the web, modern software for the web increasingly relies on dedicated services to perform processor intensive tasks. This has been encouraged further by the introduction of cloud services from Amazon, Google and several others enabling developers to considerably reduce the processor load on their servers. Each service is usually designed as a separate piece of software that runs in its own domain using its own resources.
When working with small budgets, it is generally much harder to convince clients of the benefits of funding more than one complete piece of software. In these situations I have found that many clients conclude that scalability is not a concern. They "look forward to the day when they will have to worry about scaling".
To reduce the initial investment, usually it is decided that the application should designed to be one holistic piece of software containing all the required features. This represents a potential point of failure if the software becomes very popular in a short timeframe. I have painful memories of refactoring existing codebases that have not scaled well. It can also be very costly in time and resources to re-architect software that not scaled well. Ideally applications should grow organically as required and without large sums of money being exchanged in the process.
The Hierarchical-Model-View-Controller (HMVC) pattern is a direct extension to the MVC pattern that manages to solve many of the scalability issues already mentioned. HMVC was first described in a blog post entitled HMVC: The layered pattern for developing strong client tiers on the JavaWorld web site in July 2000. Much of the article concentrates on the benefits of using HMVC with graphical user interfaces. There has been some suggestion that the authors where actually re-interpreting another pattern called Presentation-Abstraction-Control (PAC) described in 1987. The article in JavaWorld provides a detailed explanation of how HMVC can aid in the design of desktop applications with GUIs. The focus of this article is to demonstrate how HMVC can be used to create scalable web applications.
HMVC is a collection of traditional MVC triads operating as one application. Each triad is completely independent and can execute without the presence of any other. All requests made to triads must use the controller interface, never loading models or libraries outside of their own domain. The triads physical location within the hosting environment is not important, as long as it is accessible from all other parts of the system. The distinct features of HMVC encourages the reuse of existing code, simplifies testing of disparate parts of the system and ensures that the application is easily enhanced or extended.
To successfully design applications that implement the HMVC pattern, it is critical that all of the application features are broken down into systems. Each system is one MVC triad within the larger HMVC application, independently managing presentation and persistent storage methods. Presently few frameworks are available that support HMVC without additional extensions, or use inefficient Front Controllers and dispatching. Kohana PHP version 3 is a framework that was designed from the ground up with HMVC at the core. I will be using Kohana PHP 3 for all of the code examples in this document.
Kohana 3 uses the core request object to call other controllers. Requests can be made internally to application controllers or externally to web services transparently using the same request class [1.The request class used in this example is currently available as part of a Kohana Core development branch within my personal github account, which can be obtained from http://github.com/samsoir/core. If using the official Kohana PHP 3.0 download, a custom extension of the request class is required]. If a MVC triad is scaled out, the request only requires modification of one parameter.
Requesting internally requires a valid route path targeting a controller and action. Creating a request to an external resource is as simple as supplying the full URL. This feature makes internal and external requests quickly interchangeable, ensuring that scaling out triads is a relatively simple task.
Using the Kohana Request class to provide data from internal controllers may seem similar to action forwarding in other frameworks, such as the Zend Framework. In reality the two methods are quite different. Kohana Requests have the ability to operate as unique requests in isolation. Forwarding actions do not operate in this way, each invoked controller action exists within the originating request. To demonstrate this, consider the example below.
Default Controller – /application/controllers/default.php
Log Controller – /application/controllers/log.php
The example above demonstrates the independence afforded to the Request object. The initial request invokes the Default controller index action from a GET request, which in turn invokes a POST request to the Log controller access action. The index action sets three post variables which are not available to the global $_POST variable from the first controller. When the second request executes, $_POST has the post variables we made available to that request. Notice how after $log->execute(); has finished within the index controller, the $_POST data is not there. To do dynamic interaction of this kind within other frameworks requires creating a new request using a tool like Curl.
Gazouillement, the status service with a continental twist
To demonstrate the power of Hierarchical-MVC lets look at an example of a hypothetical status update service called Gazouillement, which works in a similar way to Twitter. Gazouillement has been designed around a service-oriented-architecture (SOA) to ensure the message and relationship engines are disparate from the web interface.
Traffic to the server will be relatively light initially. The service is new with an audience completely unaware of its presence. So it is safe to allow all of the application logic to execute on the same server for the time being.
Lets implement the controller to display a users homepage. A user homepage will show their most recent status messages, plus a list of people the user is following.
Index Controller – /application/controllers/index.php
Now a review of what Controller_Index::action_index() is doing. Initially the action attempts to load a user based on the user parameter of the url. If the user fails to load, a 404 page is displayed. A new request for messages is created using the users name property as a parameter within the request uri, asking for a response in xhtml format. Another request for the users relations is made in a similar manner, again in xhtml format. Finally a view object is created with the user, messages and relations responses set to it.
As the existing messages and relations are loaded using a new request, the entire application logic for each service remains abstracted from the web site. This architecture provides two significant advantages over traditional controller execution;
- The controller is not concerned with any part of the messages service execution logic. The controller only requires that the result is xhtml formatted. No additional libraries or extensions were loaded within the controller execution.
- Each controller is only responsible for one particular task, ensuring that writing unit tests for controllers is significantly less complicated.
Due to the abstraction currently demonstrated, it is impossible to see what the services are doing. So lets look at the messages service controller, starting with the route defined in the bootstrap to internally handle requests for messages. The Kohana Route class handles internal url parsing, mapping supplied uri elements to controllers, actions and parameters.
Route setup – /application/bootstrap.php
This sets a route for the messages service, presently located within the main application domain. The url request for messages/find/samsoir.xhtml will be routed to the messages controller, calling the find() action and passing 'user' => 'samsoir', plus 'format => '.json' as parameters.
Messages Controller – /application/controllers/messages.php
The detail of how user messages are retrieved is demonstrated within the Controller_Messages controller. All of the methods and properties are exclusively related to the messages context, including the relationship to users. Lets step through the messages controller code to understand what is happening.
The request object initially invokes the before() method ahead of the defined action. This allows code to execute ahead of any action, normalising common controller tasks. The before() method first tests the file format requested is supported, followed by a test to ensure the user is valid. Assuming before() completed without exception, the request object will invoke the find() action. find() loads the users messages as a Model_Iterator object. It is important to note that the iterator will be empty if no messages in the relationship were found. Finally the messages iterator is passed to a parser method _prepare_response() that correctly formats the data for output and sets any headers that are required.
The two controllers; Controller_Index and Controller_Messages; were both executed by a single request to the application. However each controller was unaware of the others presence. Any developer could read the current codebase and understand what is executing and where, providing another great feature of HMVC - maintainability.
After completing the code for the other services, the company directors are happy with the first iteration of development and green-light deployment to a live server for a limited beta trial. After a couple of weeks, the application is ready for open use. The following months see further enhancements and optimisations across the entire architecture as the customer base steadily grows.
The Stephen Fry Effect
Stephen Fry (@stephenfry) is currently one of the most famous users of Twitter, boasting a formidable 1.3 million (and counting) followers. In the past Stephen has been capable of disabling web sites just by tweeting a url to the world, in essence creating a Distributed Denial-Of-Service (DDOS) attack on the server residing at the end of the link.
Gazouillement has seen steady growth consistently for the past few months. Although the response time for most page requests has increased marginally, they are currently well within the acceptable standards. Then a twitter user with a follower count similar to Stephen Fry's, suddenly posts a message containing the url for Gazouillement. The application is in real trouble.
The first hurdle to overcome would be the shear volume of traffic. The application would suddenly have to handle thousands of requests per second, from only a few hundred per second previously. In this scenario Gazouillement would most likely overload and potentially crash the server. It is clear the application requires some optimisation and enhancement to handle considerably more traffic.
Analysing code to enhance performance is far from a simple task. Previous articles on TechPortal have demonstrated the use of tools such as XHProf to provide deep memory and CPU analysis during code execution. Kohana PHP comes with an internal benchmarking tool called the Profiler, which is supplied in the core. The Kohana profiler is complimentary to XHProf or similar tools, not a replacement. It helps developers identify slow parts of Kohana's execution that can subsequently be examined in greater detail using XHProf, or an internal module called Codebench that is supplied with the framework.
Enabling the profiler in Kohana requires the modification of a single parameter within the bootstrap.
Bootstrap – /application/bootstrap.php
Finally, add the Profiler view to the end of the controller response. This is best done by adding an after() method to your controller. This will ensure the profiler is displayed for all actions.
Index Controller – /application/controllers/index.php
Once the profiler is enabled, the output will appear at the foot of every action within that controller. Best practice is to extend the Kohana Controller and apply the profiler view within the extended classes after() method. Now, all the system controllers are outputting profiling data during development.
The profiler reports on the execution of the framework, grouping various contexts together, including; initialisation; requests; database queries; and other operations juxtaposed with memory allocation and cpu time. As each request created during execution is reported, spotting requests that are taking a long time to execute is much easier. Once slow requests have been isolated, tools such as XHProf can analyse the execution of slow actions in greater detail.
Scaling out gazouillement
Performance analysis of the whole of the Gazouillement application reveals that the retrieval of messages is causing massive bottlenecks. The development team refactors and optimises the messages MVC triad as much as possible, but the required performance gains are not met. After exhausting all the vertical scaling options including upgrading the server memory and processors, the company directors agree to scaling out the application starting with the messages system.
In traditional MVC applications, a new service has to be designed, developed, acceptance tested and then deployed. This process can take weeks and months, requiring significant investment. Essentially it is new piece of software that will be integrated into the Gazouillement app.
Hierarchical-MVC applications can create new services from the existing codebase in a fraction of the time. All interactions with the messages service were through the main controller, so the only modification to the current codebase will be to the requests for messages. The messages service is migrated to another server and optimised for database interactions. This new server only executes actions relating to messages, increasing the performance of all message related operations considerably.
Index Controller – /application/controllers/index.php
The code above highlights the tiny change required in the Controller_Index::action_index() controller serving the public website. Rather than the messages request referencing an internal controller action, the request now points to the messages service running on a subdomain called messages.gazouillement.com. What would usually be a projected as a major architecture overhaul has become a minor alteration. The associated testing and acceptance overhead is considerably smaller as not much code has changed.
This is just one example of how the Hierarchical-Model-View-Controller pattern enables software engineers to design web applications, which can scale both vertically and horizontally from the day one. We saw how the request object can be interrogated allowing controllers to return correct data for each context using a simple interface. Finally we have seen how traditional MVC triads can be scaled, and witnessed how the request object in Kohana makes scaling out a simple task. The costs involved scaling the application have been relatively small due to minimal alterations, keeping the company directors very happy.