BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Contrasting Backbone and Angular

Contrasting Backbone and Angular

This item in japanese

Lire ce contenu en français

Bookmarks

Contrasting ideas and tools is a great way to understand them better.

In this article I will go down the list of the things we have to deal with day to day when building web applications, and show how Backbone and Angular can help with each of them.

What We are Trying to Solve

Most of the things we do as web developers fall into one of the following categories:

  • Implementing business logic

  • Constructing the DOM

  • Implementing view logic (declarative and imperative)

  • Syncing up the model and view

  • Managing complex UI interactions

  • Managing state and routing

  • Creating and wiring up components

Not surprisingly most client-side frameworks address these in one way or another.

Backbone

Let's start by looking at what Backbone gives us to solve these problems.

Business Logic

Backbone Models and Collections

Constructing DOM

Handlebars

Declarative View Logic

Backbone Views

Imperative View Logic

Backbone Views

Sync up Views and Models

StickIt

Managing UI Interactions

JS Objects or Marionette Controllers

Managing State and Routing

Backbone.Router

Creating and Wiring Up Components

Manually

For visual people:

When I Say Backbone...

Comparing vanilla Backbone with Angular would not be fair. So by Backbone I actually mean Backbone + Marionette + add-ons.

Business Logic

A large chunk of the business logic of the application goes into Backbone models and collections. Often these objects correspond to resources on the backend. On top of that, they are used for backing up views.

Having to extend Backbone.Model and Backbone.Collection adds quite a bit of complexity.

First, it separates all domain objects into POJOs and Backbone models. POJOs are used when rendering templates and talking to the server. Backbone models are used when observable properties are needed (e.g., setting up data bindings).

Second, it promotes mutability. Since Backbone does not support observing functions, every computed property has to be reset when any of the source properties changes. This adds a lot of accidental complexity, which results in the code that is hard to understand and test. On top of that, all the dependencies have to be explicitly specified in the form of on("change:sourceProperty, this.recalculateComputedProperty).

Constructing the DOM

Backbone uses template engines to construct the DOM. In theory, you can plug in any engine you want. In practice, though, Mustache and Handlebars are the ones that usually get used for large applications. As a result, templates in Backbone are often logic less and string-based, but they do not have to be.

View Logic

The idea of dividing the view logic into imperative and declarative is an old one (goes back to the original MVC pattern). Event handling configuration and data bindings are declarative. Event handling itself, on the other hand, is imperative. Backbone does not draw a strict line between the two. Both go into Backbone.View.

Syncing Up the Model and View

Due to the minimalist nature of Backbone, there is no built-in support for data bindings. That is not an issue for small projects, where the view can be made responsible for syncing up the model and the DOM. It, however, can get easily out of control when the application grows.

There are several add-ons available (like Backbone.StickIt) that help unload this burden, so you can focus on complex interactions rather than the trivial model-view synchronization. Most of these add-ons are configured using plain JavaScript, which enables building abstractions on top of them matching the needs of your application.

The downside of using data bindings in Backbone is that they depend on observable properties, whereas template engines use POJOs. Having these two ways of working with the DOM often results in code duplication.

Managing Complex UI Interactions

All UI interactions can be split into simple (managed using the Observer Synchronization) and complex (where the Flow Synchronization) is required).

As mentioned above, simple interactions get handled by Backbone.View using data bindings and event handlers. Since Backbone does not have any prescribed solutions for orchestrating complex UI interactions, you are free to choose the one that fits your application the best. Some use Backbone.View, but I recommend against doing that. Backbone.View already does too much. Supervising Presenter is the pattern I tend to use for managing complex interactions.

Managing State and Routing

Backbone comes with a very simple implementation of the router. It provides no support for managing view and the application state. Everything has to be done manually. That is why in practice other libraries (e.g., router.js) are often get used instead of the built-in router.

Creating and Wiring up Components

In Backbone you get the freedom to create and wire up components in the way that fits your application best. The downside is the amount of boilerplate you have to write, in addition to the discipline required to keep the code well organized.

Angular

Now, let's contrast it with how Angular approaches the same problems.

Business Logic

JS objects

Constructing DOM

Directives

Declarative View Logic

Directives

Imperative View Logic

Controllers

Sync up Views and Models

Built-in mechanism

Managing UI Interactions

Controllers

Managing State and Routing

AngularUI Router

Creating and Wiring Up Components

Dependency Injection

For visual people:

Business Logic

Since Angular does not use observable properties, it does not restrict you when it comes to implementing the model. There is no class to extend and no interface to comply to. You are free to use whatever you want (including existing Backbone models). In practice, most developers use plain old JavaScript objects, which yields the following benefits:

  • All domain objects are framework-agnostic, which makes reusing them across applications easier.
  • They are close to the data that is being sent over the wire, which simplifies the client-server communication.
  • They are used to render views, so there is no need to implement toJSON.
  • Computed properties are modeled as functions.

The Template and View

The template in Angular is a piece of the DOM before it gets compiled. During the compilation Angular transforms that DOM subtree and attaches some JavaScript to it. The result of this compilation is another DOM subtree, which is the view. In other words, you do not create the view yourself. Angular does it by compiling the template.

Constructing the DOM

Backbone clearly separates the construction of the DOM from the view logic. The first one is done using a template engine; the second one is done via data bindings and imperative DOM updates. Angular, on the other hand, does not separate the two. It uses the same mechanism, directives, to construct the DOM and define declarative view behavior.

View Logic

Angular, however, draws a line between declarative and imperative view logic. The former is done by the view, and the latter by the controller.

This separation may seem arbitrary, but it is actually quite important.

First of all, it clearly identifies what has to be unit tested. The declarative logic encoded in the template (such as using ng-repeat) does not need tests. Writing tests for the controller, on the other hand, is usually a good idea.

Second, all dependencies go in one direction: from the view to the controller. Thus, the controller is unaware of the view or the DOM. It enables code reuse and simplifies unit testing. Contrast it with Backbone.View that often manipulates DOM nodes and rerenders large parts of it using template engines.

Syncing Up the Model and View

Angular has a built-in support for data bindings, which, in contrast to most client-side frameworks, does not rely on observable properties and, instead, uses dirty checking.

The Angular dirty checking approach has some nice properties:

  • The model is oblivious of the fact that it is being observed.
  • There is no need to specify dependencies between observable properties.
  • Functions are observable as well.

But it also has some drawbacks:

  • When integrating third-party components or libraries, you have to make sure that Angular sees the changes those make to your models.
  • In some situations it can have a negative effect on performance.

Managing Complex UI Interactions

As already mentioned, the controller is responsible for implementing the imperative logic of UI elements. On top of that, it can be used as a Supervising Presenter to coordinate complex UI interactions.

Managing State and Routing

Similar to Backbone, the built-in router in Angular is very basic and not sufficient for building real applications. Thankfully, there is the AngularUI Router project. It manages the application state, views, and supports nesting. In other words, it does everything you would expect from the router. But you are not limited to it. As with Backbone, other routing libraries (e.g., router.js) can be used as well.

Creating and Wiring up Components

Angular has an IoC container, which, like dependency injection in general, forces you to write modular code. It improves reuse, testability, and helps get rid of a lot of boilerplate. The downside is increased complexity and reduced control over how components get created.

Summing Up

That was a short overview of how Backbone and Angular address the main problems we deal with everyday when building web applications. The two frameworks have very different solutions to some of these problems. Backbone gives you more options when it comes to rendering templates, setting up data bindings, or wiring up components. Angular, on the other hand, has prescribed solutions for these problems, but is less opinionated when it comes to how you build your models or controllers.

About the Author

Victor Savkin is a software engineer at Nulogy. He is interested in functional programming, the web platform, and Domain Driven Design. He works on large applications written in JavaScript. Being a language nerd he spends a lot of his time playing with Smalltalk, JS, Dart, Scala, Haskell, Clojure, and Ioke. He blogs about building large applications in Ruby and JS at victorsavkin.com. You can follow Victor on Twitter @victorsavkin.

Rate this Article

Adoption
Style

BT