Skip To Main Content

Organizing Your Backbone.js Application With Modules

Posted by Irene Ros

Oct 06 2011

If you have spent any time looking at Backbone.js, like many others, you are probably amazed by how lightweight, flexible and elegant it is. Backbone.js is incredibly powerful, but not prescriptive in how it should be used. With great power comes great responsibility, and if you’ve tried to use Backbone.js for a large project you might be asking yourself: how do I organize my code?

When thinking about “code organization” the questions you might be asking are:

  1. How do I declare and invoke Backbone types?
  2. How do I manage a separation of concerns?
  3. How do I define a clear entry point to my application?
  4. How do I pick a clear and consistent pattern to organize the code on the file system?
  5. How do I actually name my models, collections, views and routers?

All good questions! Is there an accepted answer? Nope.

There’s been a lot of debate about this issue in the Backbone.js community for several reasons. First, JavaScript, unlike Java or C, does not have inherent code organization mechanisms such as modules, namespaces or packages, leaving it up to each developer to sort it out for themselves. Second, it’s a complex problem that does not warrant a universal solution. All applications are not created equal and the reality is that the answer probably consists of a collection of patterns that can be used as necessary given the situation. At the end of the day the goal should be to create a code-base that is easy to understand, implement and maintain.

Having said all this, let’s dive into some of the details. First, you need to put your files somewhere on your file system. The physical layout is an important question often overlooked, but the days of “all code in a single file” are over, folks.

Here’s how you might layout your application structure:

.
├── assets
│   ├── css
│   │   └── main.css
│   ├── images
│   │   └── header.gif
│   └── js
│       └── libs
│           ├── backbone.js
│           ├── jquery.js
│           └── underscore.js
├── index.html
└── src
   ├── application.js
   └── modules
       ├── friend.js
       └── message.js

Static Assets

Your static assets such as CSS, images and JavaScript libraries required by your code should go under a parent directory, such as the assets directory in our example above. Separating your library code from your actual application code is an important step – it ensures everyone agrees on the baseline and dependencies and it prevents unintentional modifications to library code.

Application code

Your application code should be under its own directory, such as a src directory. To enable a single point of entry, you should have a single file responsible for initializing the application (perhaps firing on DOM ready?) That file may also contain utility and helper methods, or those may live in their own util.js or helper.js respectively.

The rest of your application code should be divided into modules that can live under their own modules directory. A module is an encapsulated group of structures (for the purposes of our post, Backbone structures) that work cohesively to provide a subset of functionality in your application.

Index.html

Your index file should import libraries in the following order:

  1. Third party libraries included in their dependency order (e.g., Backbone.js requires Underscore.js to already be loaded)
  2. Your application.js file which is the entry point for your application.
  3. Your application supporting files, like your modules.

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>Example</title>
  <link rel="stylesheet" href="/assets/css/main.css">
</head>

<body>
  <div id="content">
    <!-- ALL YOUR CONTENT HERE -->
  </div>

  <!-- Third-Party Libraries (Order matters) -->
  <script src="/assets/js/libs/jquery.js"></script>
  <script src="/assets/js/libs/underscore.js"></script>
  <script src="/assets/js/libs/backbone.js"></script>

  <!-- Application core (Order matters) -->
  <script src="/lib/application.js"></script>

  <!-- Modules (Order does not matter) -->
  <script src="/lib/modules/friend.js"></script>
  <script src="/lib/modules/message.js"></script>
</body>
</html>

Application.js

Your Application.js has several important roles. It serves as the entry point for your application by doing the following three things:

  1. It defines the namespace of your application. For example, if your chat client application is called FireTalkChat, your namespace might be chat. All your application source should live under chat to prevent any collisions with other libraries.

  2. When referencing a module anywhere in your code, you need to make sure that those references all point to a single shared object. There are several ways to do that:
    First, a namespace object can be created underneath which each property serves as a “module”.

naive.js

var chat = {
  friend: { Views: {} },
  message: { Views: {} }
};

While this method works (and is the simplest way to start organizing your code,) there are several drawbacks to this approach. Each time a new module is added, the original “namespace” object needs to be updated with a new property. Additionally, the initialization is repetitive in this case, especially if we know each of our modules should have the same base structure. Lastly and most importantly, calling chat.friend = null, will obliterate your friend module. While modules should be in a mutable state, they should not be exposed in a way that will allow for their overwriting.

To address these issues you can create a reusable function that can retrieve the shared object reference when called. This function will create the shared object and “remember” it internally in connection with a key. This technique is called Memoizing. This way, every subsequent call will return the same object. By creating a closure around the module’s objects, you guarantee that it can’t be tampered with, and create a private scope for your module where you can define module-specific data.

application_module.js

var chat = {
 // Create this closure to contain the cached modules
 module: function() {
    // Internal module cache.
    var modules = {};

    // Create a new module reference scaffold or load an
    // existing module.
    return function(name) {
      // If this module has already been created, return it.
      if (modules[name]) {
        return modules[name];
      }

      // Create a module and save it under this name
      return modules[name] = { Views: {} };
    };
  }()
};

3. Last but not least, this file defines the DOM ready callback. Once all the scripts are loaded, this function will execute and can initialize your application.

application_ready.js

// Using the jQuery ready event is excellent for ensuring all
// code has been downloaded and evaluated and is ready to be
// initialized. Treat this as your single entry point into the
// application.
jQuery(function($) {

  // Initialize your application here.

});

Adding Your Modules

After having written your application.js, you’re ready to begin writing your modules. The modules should be named in a way that describes their intended purpose, for example:

  • A friend module
    • Model (a single friend object)
    • Collection (a collection of friends
    • Views:
      • Quick (a single rendering of the friend)
      • Detail (perhaps another view for the same model that shows more detail)
      • List (a buddy list rendering)
    • Router (a group of routes specific to the friend module)
  • a message module may contain:
    • Model (a single message object)
    • Collection (of messages)
    • Views:
      • Message (of a single message)
      • List (of a collection of messages)
    • Routes (a group of routes specific to the messages)

When asking yourself whether to create single or multiple files to contain your modules, keep in mind that for development purposes, having multiple files is cleaner. However, that requires paying special attention to load order and having a build tool that can combine all the files for your production environment as downloading many files can bottleneck a page load.

Defining your module using the above memoizing technique requires the following steps:

  1. Create an immediately invoked function expression (IIFE) to create a module-specific scope:

friend_shell.js

// src/modules/friend.js
// Module reference argument, assigned at the bottom
(function(chat, Friend) {

  //...

})(chat, chat.module("friend"));
  1. Define your dependencies at the top of the module, so that they are all grouped in a single location. Make sure to include the script tags for any dependencies before the script tag of the module that requires them. This is also a good opportunity to shorthand any application references that would be long otherwise.

friend_shell_deps.js

// src/modules/friend.js
// Module reference argument, assigned at the bottom
(function(chat, Friend) {

 // Dependencies
 var Message = chat.module("message");

 // Shorthands
 // The application container
 var app = chat.app;

})(chat, chat.module("friend"));

3.Define the Backbone structures contained within your module. Note that you shouldn’t use the “new” keyword when referencing other modules as they might not have loaded yet. Save all the code that must run on page load for the jQuery ready function in your application.js.

friend.js

// src/modules/friend.js
// Module reference argument, assigned at the bottom
(function(chat, Friend) {

 // Dependencies
 var Message = chat.module("message");

 // Define a friend
 Friend.Model = Backbone.Model.extend({
   initialize: function() {
     // Add a nested messages collection
     this.set({ messages: new Message.List() });
   }
 });

 // Define a friend list
 Friend.List = Backbone.Collection.extend({
   model: Friend.Model
 });

})(chat, chat.module("friend"));

message.js

// modules/message.js
// Module reference argument, assigned at the bottom
(function(chat, Message) {

 Message.Model = Backbone.Model.extend({
   defaults: {
     unread: true
   }
 });

 Message.List = Backbone.Collection.extend({
   model: Message.Model
 });

})(chat, chat.module("message"));

While there are many approaches to this, we found this to be a useful set of guidelines to work within. Given that the conversation about organizing your Backbone.js code is flourishing in the community, we’d love to hear your thoughts about this approach.


This post focused on the foundations of code organization, but given Backbone’s flexible nature, there are many best-practices we could tackle, such as build process, subrouting, stubbing etc. Stay tuned for additional posts and if you are interested in diving deeper, find out more about our “Building Web Applications with Backbone.js” training here: https://bocoup.com/education/classes/building-web-applications-with-backbone/

Comments

We moved off of Disqus for data privacy and consent concerns, and are currently searching for a new commenting tool.

Contact Us

We'd love to hear from you. Get in touch!