Platform vs. Template: Branching Logic, Application Architecture, And Request Optimization
I've been thinking a lot lately about how to both organize and architect a web-based application that can target multiple platforms. Specifically, I have been trying to figure out how to cater to both the traditional web experience and the mobile web experience in a way that is clean and easy to maintain. Up until now, the applications that I've built have been optimized for one platform or the other, but not both. As such, the idea of building this kind of flexibility into a single code-base seems somewhat foreign.
My blog, which is where I do most of my research and development (R&D), runs on an event-driven front controller. That is, my index.cfm (front controller) file looks at the requested event (ex. blog.list) and then hands the request off to the appropriate module controller which populates the request before the front controller then renders it. The work flow goes something like this:
- Request comes in.
- Front controller examines event.
- Front controller hands control off to event-specific controller.
- Event-specific controller gathers data and prepares viewlets (sub-templates).
- Event-specific controller selects rendering template.
- Front controller renders selected template using prepared viewlets.
The key takeaway here, for our conversation, is that the event-specific controller (or module controller) gathers the data for the rendering template that it has selected. The front controller doesn't know anything about how events map to templates, it only knows how to route events to the appropriate event-specific controller.
Assuming this approach is sound, which I think it is mostly, this gets us to the more important question: is platform-specific (ie. desktop vs. mobile), optimized rendering simply a different rendering template? Or, is platform-specific optimization something "bigger"?
To put this question in different terms, is platform-specific rendering a responsibility of the front controller or, is it a responsibility of the event-specific controller?
Before we get into this discussion any further, I just wanted to touch quickly upon CSS media queries. Media queries are a way to conditionally activate different stylesheets based on the environment of the request. That is, for example, if a request is being made on a mobile platform, you can activate a different stylesheet which can rearrange existing UI elements to fit more appropriately on the smaller screen.
I think media queries are great for helping existing content deliver a more platform-appropriate experience. But, this is a solution that gets invoked after the page has been delivered to the client. That is, this is an approach that gets applied after the database has been queried and the templates have been rendered; as such, while it is good for the user, it offers little in the way of processing optimization.
What I want to explore here is the way in which we can optimize both the request processing and the response rendering. As such, while media queries are useful, they are not terribly relevant to this conversation.
But, getting back to the question at hand, at first I thought maybe mobile rendering was just another template. That is, if my blog module received page control, it could look at the type of platform making the request and then sub-route the request to a platform-specific processing unit. After reflecting heavily upon this choice, however, I believe this approach has two critical drawbacks:
- It makes naming much more difficult because while both desktop and mobile platforms may respond to a "blog.list" event, you cannot have two blog.list templates or controller methods (depending on how your controller is implemented). As such, either one or both of your controller hooks must contain the platform as part of its specification.
- Leaving platform rendering up to the module controller offers no affordance as to what content is available for a given platform. That is, as a developer, it becomes very difficult to look at the code base and figure out which modules can cater to different platforms. For example, I can't tell how much of my application is available for the mobile platform without looking at every single module controller.
It seems that platform-specific rendering doesn't work too well at the event-specific controller level; so, what about the front controller level? Well, as it turns out, I think I've already been using this approach with my API architecture.
I am relatively new to API development, but when I do provide a public API within an application, I tend to place the code for it in a root level "api" folder:
www.somedomain.com/api/
You may be asking yourself, what does an API have to do with platform-optimized rendering? Well, what is an API? Could you perhaps think of an API as the controller for a platform that only understands JSON, JSONP, and XML?
I know a lot of people think that an API is something very "special". And, it is. But, is it really any more special than a traditional web experience or a mobile web experience? I would argue that it is not. Yes, it might have its own rules like authorization requirements and usage throttling; but, these rules don't make it special; they simply just characterize the platform experience in the same way that a "friendly 404" page might characterize the traditional web experience.
So, what might the code organization of a root-level platform divergence look like? Perhaps something like this:
- index.cfm (front controller)
- api/
- desktop/
- mobile/
Here, we have our front controller and then three different platforms: api, desktop (for lack of a better term), and mobile. I assume each of these platforms would have its own front controller to which the root front controller would pass off control.
But what about our business layer? We don't want to repeat our business logic or our application may become susceptible to error and maintenance difficulties. Ok, let's put our components, or "com" folder, in the root where it is available to all platforms (NOTE: I'm using a web-accesible location for ease of demonstration):
- index.cfm (front controller)
- com/ (business layer)
- api/
- desktop/
- mobile/
Now, what about our rendering-related files? Our Javascript, CSS, and images? Typically, I would put these in the root using either a "linked" folder or an "assets" folder or something to that effect. But do these make sense in the root? Does the API need access to Javascript and CSS files? Furthermore, do the mobile templates need access to the desktop CSS files? I would posit that they do not. As such, I think it makes sense to place our rendering helpers inside the specific platforms:
- index.cfm (front controller)
- com/ (business layer)
- api/
- api/templates/
- desktop/
- desktop/linked/
- desktop/templates/
- mobile/
- mobile/linked/
- mobile/templates/
As you can see, each platform gets its own set of rendering helpers and rendering templates. At first, you might think this will cause redundancy - after all, do we really need to put a copy of jQuery.js in each platform? Maybe, maybe not. Maybe the desktop platform uses jQuery while the mobile platform uses a lighter weight library like underscore.js. The point is, each platform has its own specialized requirements which may or may not have some overlap. In the end, I think stressing over the minor duplication of libraries is nothing compared to the benefit of easily allowing all platforms to evolve independently over the life time of an application.
At this point, I don't want to get any more detailed about how files will be organized within each platform. Every framework and methodology has its own way of approaching Views and Controllers so getting any deeper into my own preference doesn't really add much value to the conversation.
Right now, this is all just theory that I have codified over the last few hours (this took a long time to think about and then to write). If you have any feedback on this, I would love to hear it. Like I said in the beginning, I've never really written an application that catered to multiple platforms in an efficient way. I think people very much want it to be easy to cater to multiple platforms; but, at the end of the day, I don't think it is. I think the best we can aim for at this time is simply to create an architecture that makes understanding the implications of the code base easy for developers to manage.
Reader Comments
Great article!
The file placement/naming as you said is very framework specific. So there is no standard way to do this. Although in the past I did something similar with js/img/css files that where hosted on CDN's:
cdn1.somedomain.com/
web
/w/1.0/img/
/w/1.0/js/
/w/1.0/css/
mobile
/m/1.0/img/
/m/1.0/js/
/m/1.0/css/
desktop
/d/1.0/img/
etc
I used w for WEB, m for MOBILE and d for DESKTOP.
Also I've added version numbers to the mapping to have older version available if for instance someone still uses the older desktop client.
The main thing you're looking for is a onSessionStart check that checks the type of client it is and then sets a session var that will give the rendering controller a way to know which version of the template to use. Like in your example:
blog.list is called and based on the session variable you could use blog.list.m.cfm (mobile) or blog.list.d.cfm (desktop) or default to the web version blog.list.cfm
just my 2 cents.
I'm also in the midst of trying to digest the best approach for catering to the web and mobile under a single app. My first thought was that I could just simply style the same markup for each platform. That proved to be a little more difficult than I anticipated even though I'm sure some more CSS savvy folks could do it.
However I'm also faced with the same controller level decisions you mentioned where not all functionality is needed when viewing under the mobile guise vs from a desktop (following the Minimal Viable Product approach). Do I put in conditionals in my controller to determine which services to make use of (ie. if( notMobile ){ /*get extra stuff*/ }) or cleanly separate them along the lines that you have described? I'm not entirely sure yet the direction I will take but most likely I will just pick one and go, then refactor later if I see more benefit in the opposite approach.
One additional thought that I had regarding full separation of resources per platform as you mentioned:
Placing resources such as JS and CSS templates in separate respective platform folders (ie desktop, mobile) does lead to redundancy as you mentioned, but worse, it can make re-use of some of those components very difficult. There might be common styles or JS functionality that is platform agnostic and depending on how much overlaps it could be a bear to maintain if duplicated per environment.
Great article as always.
@Michael,
Actually, you bring up a fantastic concept that I had not even thought of for my post: versioning. By separating the platforms at the root level, it makes versioning an entire platform much easier to understand and to maintain. Take something like the API, for example. When dealing with APIs, we will often see notation like this:
/api/2008-10-01/
/api/2010-04-04/
Here, the API is being versioned for compatability. Had be tried to do something like this within each modules, can you imagine the amount of scattered folders we would have all over the place?
As far as checking what kind of platform to serve to the user, I like the idea checking within onSessionStart() this way, the check is done when the user gets to the site only once, thereby allowing them to switch to an other platform if necessary (sometimes the mobile version doesn't have all the functionality and you want to take the performance hit to access a given feature of another platform).
I was thinking of perhaps have a "preferredPlatform" and "currentPlatform" variable and then, if these two values don't match (ie. the user has switch to another platform), I can perhaps put a link at the top of the site with something like:
"View Mobile Edition"
... or something like that.
@JAlpino,
I had also considered the if(!mobile){..} approach as well; but, I think that will just become a maintenance nightmare. This is just a gut feeling, not something I've tested. But, I just don't think that a file with lots of conditional branching is going to be good over the long term.
As far as redundancy in platforms, again this is not tested, but my gut is telling me that this fear is emotional and not practical. Think about the jQuery Mobile framework we saw at jQuery Conference 2010 - how much does that actually share, markup/CSS/JS, with the desktop version? From what I can see, almost none. Both environments are completely catered to in a different manner.
That's why I think trying to share public assets (ie. not business components / CFC / jar files) across platforms is gonna end up being a premature optimization that will actually make maintenance harder in the long term.
Plus, it definitely increases the amount of regression testing you have to do. Imagine you want to upgrade from jQuery 1.4.2 on desktop to 1.4.3. Then, not only do you have to check the desktop platform, you might have to check the mobile version for bugs as well (assuming both platforms share the same jQuery library).
@Ben,
I think with either approach, conditional branching or maintaining two sets of code, there is the chance that either choice could become a maintenance issue down the road. If your apps become very different from each other, then branching will surely cause some pain, on the other hand if the apps are near identical (less a few rendered features for mobile viewing) then having double the code to maintain could also become a pain. I feel it's like most things in apps dev, it largely "depends" on how your app, framework, design, etc.. is set up.
With respect to jQuery however, their mobile library is just an extension of jQuery Core, without core it does not work. I have not dug too deep into their public repo to see if they are overriding any functionality from the core but as far as I was able to tell, the mobile stuff is in addition to their main library, not a duplication of stuff.
To clarify my earlier comment in terms of maintaining shared client resources. As an example, the application that I am working on has a validation class which verifies user input before being checked again on the server. The validation works the same regardless of platform. A page specific controller handles what to do when that validation succeeds or fails (updates the ui, scrolls to error message, etc..). In that example, I feel that I can maintain a single instance of that validation class but have different page controllers for each of the platforms.
Just some thoughts.
A bit tangential to your main point, but within the mobile space, you might consider jquery mobile (jquerymobile.com) in order to get a consistent UI across as many mobile platforms as possible.
I attended a talk on it at the jQuery conference, but unfortunately have not actually done any development with it, so no specific real-world experience to share, but when you get that far into the development, a consistent UI within the mobile space will probably be another issue you'll need to grapple with.
@JAlpino,
Good point on the jQuery mobile framework being just an extension of the jQuery core. I think you are right on that (I honestly haven't looked at it beyond the demos at #JQCon).
But, going back to your example, I think in the best of all worlds, sharing is good. But, I think there is a power to allowing the platforms to evolve independently. It allows you think about making changed incrementally without having to worry about them affecting anything anywhere else.
I'd love to carve out some time to work on some R&D on this kind of stuff. Make a tiny app as an exploration.
@Philip,
Yeah, the jQuery Mobile stuff looks awesome. I also saw those presentations and was quite impressed!
While I am thinking these days on what is the easiest/best way to make my app Theme/Template Enabled, where each template has its own unique UI view either for mobile or desktop.
I think if I make each template as a folder, and inside there will be folders for desktop and mobile and whatever so I can easily plug in & out any template without touching several folders out of, because it should be independent. Even later I can think about how to make it possible for clients to make their own Template with a fair good knowledge in HTML, CSS and programing logic to deal with CF code if needed :) . The idea is almost similar to Wordpress Themes.
And keeping com and api out as you suggest.
The final structure is something similar to:
/com
/api
/templates/tmplt-A
/templates/tmplt-A/mobile
/templates/tmplt-A/mobile/links
/templates/tmplt-A/mobile/images
/templates/tmplt-A/mobile/...
/templates/tmplt-A/desktop
/templates/tmplt-A/desktop/links
/templates/tmplt-A/desktop/images
/templates/tmplt-A/desktop/...
/templates/tmplt-B
/templates/tmplt-B/mobile
/templates/tmplt-B/mobile/links
/templates/tmplt-B/mobile/images
/templates/tmplt-B/mobile/...
/templates/tmplt-B/desktop
/templates/tmplt-B/desktop/links
/templates/tmplt-B/desktop/images
/templates/tmplt-B/desktop/...
and so on ...
Then, if mobile and desktop in single template share common CSS,JS or images I can but them in links folder at their template root folder. The same if there is a common thing that several templates are sharing I can take it out to the website root.
Ameen
Awesome post.Here's an all-in-one business application platform. It has everything you need to build custom business apps fasthttp://www.caspio.com/application-platform/