We recently completed a large project that leveraged backbone.js — and particularly Derick Baily’s wonderful marionette.js architecture patterns.
As this was our first time using these frameworks, the entire experience was a learning process for us. Furthermore, without the benefit of experience as a guide, I was hesitant to rely too much on other ‘pre-built’ pattern libraries since I didn’t know what trade-offs I would have been committing to. One of the reasons backbone/marionette initially appealed to me for this project was the fact that it didn’t seem to demand any particular philosophy to the structuring of the core application architecture.
One area in particular that I was very interested in understanding was how best to organise the application throughout the project, specifically the view/template files. Although I found a ton of resources for backbone patterns and best practices, I first wanted to understand the basics. Using David Sulc’s great tutorials as my starting point, I dove into finding a system that made sense and was, at the same time, as scalable as I knew the final product would require.
Take a simple marionette application that displays a list of employees in an HTML table. At its most basic, a marionette template would be defined in the root HTML document, separately from your app js code, like this:
script type="text/template" id="team-member-template"> <%= name > /script
This HTML partial is then tied to a marionette view like this:
TeamMemberView = Backbone.Marionette.ItemView.extend({ template: "#team-member-template", tagName: 'td', className: 'teammember' });
Since I knew that my app was going to be structured as reusable components, I really wanted a way to organize these templates in a way that made contextual sense to their usage. Furthermore, since my components would end up being extensible, I wanted to leverage template overriding to allow me to repurpose a core set of modules, while allowing for slight variations among the views.
Ultimately, what I wanted to end up with was something that looked like this:
To achieve this, I thought, why not make the templates load ‘on-demand’? I would define a set of base templates with each component, and then, each time it was instantiated, it would then load either its base templates, or any custom templates I decided to assign to that particular view.
The component declaration would end up looking something like this:
addBlock(‘NfMessageList', ‘clientmessages’, { datasource: ‘/apex/RestClientMessages’, itemtemplate: ‘custom-list-item.html’ } );
Which would lead to a new item view being generated. There, the app would deal with loading the template(s) as needed.
ListView = Marionette.ItemView.extend({ template: _.template( App.loadTemplate( component.itemtemplate || ‘default-list-item.html' ) ),
It soon became apparent that there were major drawbacks to this approach. Since a separate AJAX call is made to load each template, the first time the app is loaded, quite a few round trips are made before even the first data can be rendered. Depending on the device/platform, this can range from annoying (2-3s initial load time) to unusable (10+ seconds on some mobile devices).
I really didn’t want to give up on my application structure, since it had proven very scalable in my development thus far. I was reluctant to give up the ability to override my templates as needed.
What I ended up learning the hard way was that, ultimately, a multi-tiered approach is what I wanted.
In the core of the application, I left the component/template model intact. That prevented costly refactoring and let me keep the flexibility I wanted. Instead, I refactored the App.loadTemplate() to basically utilize a multi-tier process:
1. On the server side, I implemented a roll-up, that parsed the app folder structure on release and produces a single, static file containing all of my partials.
2. The first time a client browser loads the page, this rollup is retrieved in one http request. The contents of this file are then regex-ed into their respective template/partial bits, and then stored in localStorage if available. I use the actual template path (as seen above) as the storage keys for the partials, no matter where it ends up living.
3. Each time a component is loaded, its template HTML is looked for in several places:
-
LocalStorage cache. The partial should be here on any modern browser
-
Global cache at the App level. The partial will be here if the user has persistent storage turned off, etc
-
An AJAX call. This is worst-case and should never happen, but if the partial really is missing (maybe the roll-up load failed), we load it here and suffer the performance hit
In the end, this turned out to be a pretty good solution for us, providing the performance we needed. More importantly, though, this process (and countless others like it) were critical in the overall success of the final product.
It is a testament to backbone.js and marionette.js that this type of flexibility was even possible. The very act of muddling through these discoveries, for me at least, provided a much higher level of confidence that we’ll be able to support and grow the app in ways that continue to be purposeful and advantageous. Plus, this freedom made the project that much more fun to build.
Further reading
http://ricostacruz.com/backbone-patterns/
https://github.com/derickbailey/backbone.marionette
http://www.9bitstudios.com/2013/05/using-templates-in-backbone-js/