This post is about using pagelets to structure dynamic web applications. I’m currently experimenting with ways to do this in Rails, and have published some of my code, but hopefully some of the ideas covered here will be applicable to other frameworks and languages.
One of the patterns described below is Hierarchical Model-View-Controller (HMVC), which is excellently described in this post from Sam de Freyssinet. The PHP web framework Kohana (which I haven’t used) is based around HMVC.
Many of the techniques discussed below have long been used in large scale applications. Part of the aim of this work is to make it easier to extract benefit at smaller scales, and provide a path to more sophisticated configurations as the application grows. This project is a bit of an academic exercise at the moment and the code I’ve written barely been used in production. Hopefully that will change soon, but until then please treat the content of this post as a discussion point rather than a description of what’s been proven to work.
Pagelets
Pagelets are chunks of a web page. I’ve taken the term from Facebook’s description of their BigPipe infrastructure, but you could alternatively call them modules, components, subpages, cells or one of a number of other terms. An example might be a list of related items on the product pages of shopping site. Pagelets are a useful architectural construct from the point of view of maintainability (as they help keep responsibilities separate) and scalability (as they provide more flexibility in scaling out).
In this post I have a fairly specific definition of the term in mind, and not all page components fit into this category. To me, the characteristics of a good pagelet are something like:
- Substantial content: Something like ‘the pagelet is complex enough to implement a meaningful domain model’.
- Independence from the containing page: A component which can be rendered using just the ID of the primary page object is a better candidate than something which shares a complex, dynamically computed data structure with another part of the page.
- Potential to be handled by a back-end service: If the content can be provided by a separate back-end service, pagelets potentially provide a useful tool for scaling.
- Cachability: If the content of a pagelet can be cached more aggressively than the containing page (or vice versa), there are potentially scalability and perfomances wins to be had.
- Localisation: Pagelets should be embedded into the containing page at a single insertion point. It may be possible to generalise the ideas in this post to relax this, but I’d like to keep things simple for now.
These are guidelines rather than rules, and invariably exactly what should and shouldn’t be considered a pagelet will depend very much on the application in question and the context in which it’s being run.
I believe most applications will have reusable interface components which it don’t really make sense to consider as pagelets, for example repeated visual elements, standard representations of domain objects and ‘widgets’ implementing common interaction patterns. It’s definitely a good idea to structure these, but they present a different set of problems to those that the pagelet pattern is trying to solve. In some cases the HTML will already provides sufficient structure, so trying to abstract them further will end up making the code less readable.
The Facebook post referenced above includes this diagram showing the pagelets on the Facebook home page, which gives some indication of the scope I have in mind.
Implementation Patterns
Once pagelets have been identified, there are multiple ways to approach the process of rendering them, assembling the full page and delivering it to the user. For example:
- HMVC: The pagelet is inserted into the containing page directly as it is rendered through delegation to a secondary MVC triad. All execution happens in process and sequentially, so execution of the containing page stops while the pagelet is being rendered.
- Parallel execution: Pagelets are rendered in process but in parallel with the main page. Rendering of the main page halts at each insertion point until the relevant content is ready.
- BigPipe: See this post on the Facebook engineering blog. Similar to parallel execution, except pagelets are delivered to the client after the main body and positioned with Javascript. This prevents blocking during rendering of the main page.
- Forwarded: The pagelet content is requested from a separate back-end server. This can be used in conjunction with any of the above three approaches.
- Edge side includes: Pagelets are inserted either by caching proxy (such as Varnish) in front of the application, or on a content delivery network.
- XHR: Pagelets are loaded client-side using Javascript to make additional HTTP reqests.
Which of these is most appropriate presumably depends on the content of the pagelet, the application it’s part of, the application’s usage patterns, the infrastructure supporting it, the skills of the team responsible for maintaining it and so on.
Rails Support
I started off with a quick search for existing projects in this area. I’d imagine that a lot of this has been done in an ad hoc fashion as required for specific products, but there have been a few publicly released tools:
- ActionController::Components: Rails up to 2.2 supported nested rendering out of the box, so you could define a separate controller and view for the pagelet. This was deprecated since 2.0 and dropped entirely in 2.3.0, apparently for performance reasons. The functionality does seems to exist as a plugin, although as far as I can tell it isn’t maintained.
- Cells: A similar idea, but uses a separate type of object for the pagelet’s ‘controller’.
- Embedded Actions: Another plugin providing functionality similar to that dropped from Rails, but with support for caching and (slightly unconventional) use of the ‘respond_to’ method to allow different behaviour when a controller is embedded.
None of these seemed flexible enough for what I wanted. Specifically, I wanted a solution which would make internal and external handling of pagelets as similar as possible and would require minimal deviation from a standard Rails application. To me this means that pagelets should use standard Rails controllers, and should be addressed by URL internally and externally. I also didn’t consider the performance issues in the original Rails implementation to fundamentally invalidate the approach: slow code can be optimised, and the dispatch infrastructure has changed a lot with the introduction of Rack in Rails 2.3.
ActionEmbedding
Enter ActionEmbedding. This is a prototype Rails plugin I’m using as a test platform. The particular design goals are:
- To make it easy to benefit from pagelets in regular Rails application without the need for additional infrastructure or significant changes to the configuration of the application.
- To make it as easy as possible to change between pagelet delivery methods as the application evolves.
See the GitHub page for technical details, but essentially inline pagelets are implemented as regular controller actions with routable URLs. Using it is as simple as including an additional module in the ApplicationHelper class:
module ApplicationHelper include ActionEmbedding::Helpers end
This provides a simple method to embed a pagelet within the view:
<%=embed_pagelet('/pagelets/two') %>
Changing the embedding method is done by providing an option hash for this method.
<%=embed_pagelet('/pagelets/two', :method => :proxy, :proxy_host => 'backendcluster.internal.net') %>
The plugin is very simple, but should work for Rails 2.2 and 2.3.
What Next?
I’ve got a lot of ideas, but really want to start using this in a real project to figure out what’s important. If you fancy giving this a go, any feedback or suggestions would be appreciated.