Posted on by & filed under Content - Highlights and Reviews, django, javascript, microservices, programming, Programming & Development, Tech, testing.

Earlier this year, we overhauled our search application and changed it from a django app to a microservice. This opened us up to move the frontend of our search out of django templates and into a single-page JavaScript application that communicates via JSON.

Marionettejs

Starting from scratch allowed us to think about the overall structure and components of the application. We use Backbone in most of our javaScript so a Backbone + Marionette + Backbone.Radio application made sense. Marionette allowed us to simplify our application in a number of ways including; better binding between our models and views using Marionette Layout, Composite, Item and Collection Views, more reusable components using Marionette Behaviors, easier view rendering and cleanup with Marionette Regions, templateHelpers for teasing logic in our JavaScript templates.

Breaking things down

We started by breaking the app into modules. This included a module for ‘facets’, which are the publishers, topics and author options you can check off in the UI to narrow your search. ‘Suggest’ for the autocomplete module. The main part of the app handled pagination and the templates, collection and views for rendering search results.

Each of these modules included their own collections, models and views. We kept these mostly isolated components that used local event messaging. We used ‘controllers’ for app-wide event messaging and managing their module components.

Simplifying views

We broke our views down using Collection and Item Views. In Marionette, a CollectionView takes a Backbone Collection and renders the individual models data inside it’s child ItemView’s. This reduced a lot of the code involved in just rendering the UI and let us focus on the collection and model logic which is fairly involved for us. It also let us keep a lot of data outside of views and in models where they are easier to manage.

We put reusable components like analytics into Marionette Behaviors for mixing into our views. Passing an options hash into the initialize method allowed for a lot of versatility.

Backbone.Radio

While Marionette helped the application become more modular and component-based, Backbone.Radio became the spine of communication for the app. Radio simplifies the Backbone.Events messaging system by allowing you to segment your messages into Channels and organize between Events (which are like notifications with no intention) and Requests (which ask for a very specific thing to occur). Our messaging bus became more focused and easier to trace through and understand complex behavior.

Request and response

Using Backbone.Radio we created a single point in the application for handling http request and response for direct communication between our app and the microservice. Some partial code for that handler looks like this,

Another part of the app will issue a request to do a search while passing in a hash Object with search data.

The Backbone.Radio reply handler passes this search data to a Model that handles it and POST’s it to the microservice. The success or error callbacks issue radio events for other parts of the application to listen to and build search results from.

Similarly, there is a central handler for rendering search results. A ‘Controller’ Object handles some of the core logic in the Views and also listens to ‘sync:success’ events in the ‘search’ Channel. On such an event, it collects all the data from the collections of facets and then issues a ‘buildResults’ request. The handler for that event builds and renders the UI from the response the microservice sends back, it looks like this,

A ‘buildResults’ request is issued from inside a handler listening to http success events,

Setting up the messaging bus like this allowed us to decouple our application following a ‘mediator’ type design pattern. Using Backbone.Radio, our modules didn’t have to talk directly to one another and avoided tightly coupled bindings that are difficult to test and debug.

Testing

We wrote a lot of tests. I like to write javaScript tests. Not because I enjoy pain (I don’t) but it’s the quickest way to debug my code without fighting the browser. Some of my favorites on this project were for testing that you can use keyboard shortcuts inside the dropdowns in the UI. One such test looked like this (we use Qunit),

Conclusion

This was a big project and if I have any advice to offer for building a large JavaScript application in Backbone + Marionette its this;

  1. Think about architecture first: break your application down into modules and then think about organizing them around a JavaScript Design Pattern that will allow it to scale.
  2. Put DOM state in models: it is much easier to manage data when it’s in models then in views.
  3. Keep events simple: think about how you use global events and use them sparingly. Prefer one-to-one event bindings such as Marionette’s triggerMethod and listening directly to other Objects.
  4. Use Controllers to make sense of related parts.
  5. Write a lot of tests: they can be difficult but they’ll make it much easier to debug your application.

Tags: backbone.radio, Django, Framework, front-end, Javascript, JSON, marionette, microservice, search,

Comments are closed.