Decoupling Backbone.js Applications with pub/sub – Safari Books Online’s Official Blog
A guest post by Tim Ruffles, a Ruby & JavaScript developer based in London who teaches and mentors developers, and works on SidekickJS, a code-quality tracker for teams. Previously he was a front-end tech-lead for Skimlinks and Picklive; he talks about Javascript at conferences and events and can be reached at @timruffles.
Backbone.js applications can become tightly coupled, however, the publish/subscribe (pub/sub) pattern is a great way to decouple these applications, and it is easy to implement the pub/sub pattern in Backbone.js.
In this post, we will look at how pub/sub makes it easier to change, test and reuse your Backbone.js code by reducing coupling. We’ll also see how it affects the structure of your application – especially making sure your Router
doesn’t become an all knowing “God object.” You can also take a look at a post on persistence in Backbone.js that details how to split your model from persistence in Backbone.js.
The problem
First, let’s talk more about the problem this pub/sub pattern solves: coupling. Coupling refers to the knowledge two parts of your code, or components, have of each other. You know two components are coupled when a change to one requires a change in the other. Coupling normally takes the form of one object accessing methods or properties on another. The problem with coupling is that if an object’s methods change, or a new object replaces it, you’ll need to update every part of your application that uses it.
Pub/sub reduces coupling, because rather than talking to other objects directly, you can publish messages on a shared pub/sub object. In Backbone.js, an example might be to play a sound on a user being logged out:
Notice we don’t know how the user was logged out, or which object was responsible, only that it happened. As you can see, the pub/sub is coupled to the name and data payload of a message. The only dependency is on the pub/sub object that has a very small API – you can send or receive messages. Which component triggers the message ceases to matter – you just ensure your system reacts to the vocabulary of events correctly.
Let’s see some code
Let’s look at a concrete example. We’re building a little tool to search your favorite tweets. You have a search box, a history view, and a results box. You can search by text, jump back to previous searches, or filter by a specific author.
Our pub/sub implementation is very simple: we just mix Backbone.Events
into an empty object:
We’ll just use the standard trigger
and on
methods to publish and subscribe to messages.
Using our pub/sub implementation we can easily make the views completely unaware of how the search process occurs. They just publish a event with a new query criteria, and a different object is responsible for creating a new search from it. If it triggers a new search, that is broadcast, and any views that are responsible for displaying the search are updated.
For instance, the search view simply triggers a criteria change on submit:
Our TweetList
listens for a new search, and sets up listeners and re-renders once it hears it:
Allowing users to switch back and forth between searches in this design is simple. The history view simply fires search changes, and we have a plain old JavaScript object to manage the process of transitioning between searches when new criteria is supplied:
So, we can see that none of these objects know about any one of the others. Their behavior can all be considered in isolation. If they do the right thing given the events firing, the system will work as a whole.
Decoupling
If we contrast this approach with a more traditional one, we can see where the decoupling occurs:
We now have two possible problems when refactoring occurs. First, the search manager’s API is exposed. Changes to this API will affect all components that were updating the criteria. Secondly, we have to pass around our search manager through layers of hierarchy that don’t need it themselves.
In testing, we now also need to pass in a custom mock or an instance of our search manager. If we change the API, that’s one more place our coupling has forced us to change our code. With the pubSub
implementation, we can completely change how the system responds to messages without a single line of application or test code changed in the components that broadcast them.
Compared to events
Pub/sub can see similar to events. The core difference is that you don’t know who’s firing the events, and you don’t need a reference to them to listen. Objects in a pub/sub system share a central pub/sub object that they fire messages on. The listener doesn’t have to know about the publisher to listen!
So, from our comparison, we can see one immediate advantage is the non-locality of handler. With events fired via the Backbone.Events
methods that are present on Backbone.View
, only objects with a reference to that view can listen. For instance: let’s say we refactor a child view that fires a “search:set” event to have a child view of its own that now is responsible for firing the event.
It’s clear we’ve had to listen to the new child view to ensure that the “search:set” event is still visible to our ParentView
. Pub/sub doesn’t have these problems, since the pub/sub object is shared throughout:
Getting access to the pubSub object
One potential question that arises is how to get the pub/sub object into our views. One answer is to pass it via constructors, so each view passes it down into its child views. This creates a lot of boiler-plate.
A better approach is to embrace Javascript’s dynamism. We can set the pubSub object on our View’s prototype, so it’s shared among all instances. To maintain testability we create a View
subclass that accepts a pubSub
in its options, and falls back to first the prototype, and then a global pubSub
object.
This has all of the ease of a global pubSub
object with none of the pain: we keep testability, and we still have the option to make multiple pubSub
objects if we need to isolate a module.
Where to use pub/sub
I use pub/sub in my views. I don’t think it makes sense to use pub/sub in model code, as the views (which do double duty as controllers in Backbone.js) are responsible for making sure models are updated in response to user commands. Models sit and handle domain logic, and controllers can listen to and publish these changes as events that need to change our views.
So: publish model events from controllers that are listening to models if required, but don’t let models use pub/sub internally – it mixes up view concerns with your domain.
Testing pub/sub code
Testing code that uses this pattern is much easier – you simply make sure components respond correctly to messages, and fire any broadcasts it should. We never have to setup complex collaborator objects because we always have just one: our pub/sub.
Pub/sub and Backbone.Router
Pub/sub also helps to reduce a system’s coupling to the router. A typical anti-pattern I see in Backbone.js code is the ‘God router,’ where the router knows about every model and view. This is a nightmare to test, maintain and debug, since the router grows with every new feature. For me, the router should simply be thought of as another View, firing events on the user’s interaction with the URL. This aligns with the “small components, each doing one thing well” approach you may have heard referred to as the Unix philosophy. A router should respond to URLs, not instantiate views.
When using pub/sub, I prefer to have no methods at all on the router and merely publish its events as view:change
events. That way, if a view change needs to be triggered for another reason – for instance undoing a command, or a user being logged out – the rest of the application can just as easily fire the view:change
event. Supporting PhoneGap and mobile websites becomes much easier this way. And finally, testing the router is made very simple as well.
Pub/sub – object orientation done right
So: if you’ve ever felt that Object Oriented code feels hard to change, and that refactoring always involves changes spreading throughout the system, I think pub/sub will help. Alan Kay’s original conception stressed messaging over the internal properties of a system’s objects. Pub/sub gets a lot closer to this ideal, and keeps your code malleable, easy to test, and simple. Enjoy!
Be sure to look at the Backbone.js resources that you can find in Safari Books Online.
Not a subscriber? Sign up for a free trial.
Safari Books Online has the content you need
Developing a Backbone.js Edge incorporates the best practices and the techniques from the combined authors’ experience of developing many Backbone.js applications. In this book you get a complete guide to Backbone.js and equip you to start using the library straight away. While writing this book the authors developed an example app, called Hubbub, to illustrate the various features of the library.
Developing Backbone.js Applications shows you how to get the job done with Backbone.js. You’ll learn how to create structured JavaScript applications, using Backbone’s own flavor of model-view-controller (MVC) architecture.
Backbone.js Cookbook contains a series of recipes that provide practical, step-by-step solutions to the problems that may occur during frontend application development using an MVC pattern. You will learn how to build Backbone.js applications using the power of popular Backbone extensions and integrating your app with different third party libraries.
About the author
Tim Ruffles is a Ruby & JavaScript developer based in London. He teaches and mentors developers, and works on SidekickJS, a code-quality tracker for teams. Previously he was a front-end tech-lead for Skimlinks and Picklive. He talks about Javascript at conferences and events and can be reached at @timruffles.