Why Backbone-Relational Sucks?

Apologies for being blunt, but this is what I truly feel after weeks of struggle with Backbone-Relational. Some of the features library boasts are great, but some other problems make its usage horrible. So if you are lured by the cool features this framework claims, here is my candid advice: keep some Disprin tablets along because it is going to give you some serious headaches.

Below are some of the problems I faced using this framework. Here is an dummy data model that I’ll be using throughout my examples. You can skip it for the moment, and refer back to it when you see it being used somewhere.

var Person = Backbone.RelationalModel.extend({
    }),
    PersonCollection = Backbone.Collection.extend({
	model: Person
    }),
    City = Backbone.RelationalModel.extend({
        relations: [{
            type: Backbone.HasMany,
            key: 'residents',
            relatedModel: Person,
            includeInJSON: Backbone.Model.prototype.idAttribute,
            collectionType: PersonCollection,	
            reverseRelation: {
                key: 'city'
            }
        }]
    }),
    CityCollection = Backbone.Collection.extend({
        model: City
    }),
    Company = Backbone.RelationalModel.extend({
        relations: [{
            type: Backbone.HasMany,
            key: 'employees',
            relatedModel: Person,
            includeInJSON: Backbone.Model.prototype.idAttribute,
            collectionType: PersonCollection,	
            reverseRelation: {
                key: 'employer'
            }
        }]
    }),
    CompanyCollection = Backbone.Collection.extend({
	model : Company
    });

Example#1

At the core Backbone-Relational, there is a method Model#fetchRelated(). This is one big fat mouse that will first appear invisible, and when you will eventually find it after hours of computational cycles of your brain, it will drive you nuts (Oops! I am again being subjective?). Here are a few use cases to explain the situation:

// let us initialize a PersonCollection with some specific ids
var models = _.map([3, 6, 9, 13], function( id ){
        return new Person({id:id});
    }),
    persons = new PersonCollection( models );


// fetch the specific ids
persons.fetch()
   .then(function(){
        // person collection is ready here
        // let's print each person with their company name
        persons.each(function( person ){
        /* 
         * person.get('employer') will return only an id,
         * in order to get a full-fledge company object, we need
         * to call fetchRelated(). But guess what, fetchRelated()
         * doesn't return the objects themselves, it returns an
         * array of promises.
         * 
         */
        $.when.apply(null, person.fetchRelated('employer'))
            .then(function(){
                // let's print our data now
                console.log('%s works in %s', person.get('name'), person.get('employer').get('name'));
            });
        });
    });

Before running this snippet, make sure that at least two of the persons mentioned in the list work in the same company. Now prepare yourself for first surprise: If two or more persons share an employer, you will see a log printed for first person alone and an error for the subsequent persons which will say something like this “Cannot call get on undefined”.

Problem Behind fetchRelated()

Google this problem and you’ll practically find nothing. If you debug the issue, you’ll find that fetchRelated() doesn’t return a promise for an entity if a promise had been already returned for it. This behavior, though looks acceptable from logical point of view, makes consumption of the API difficult, because there is no consistent interface to get a related model. Values returned by Model#get() and Model#fetchRelated() vary depending upon internal state of the framework, leaving all the checks and balances to the caller, thus entangling the application developer into infrastructural issues and blurring her focus on business logic.

Workaround

I know it sounds silly, but for the sake for argument, let’s consider this option: fetch all of the companies that you are going to use, as the page loads, so you will never need to invoke person.fetchRelated(‘employer’) and can directly get employer entity using person.get(‘employer’). But this approach becomes almost impossible because:

  1. It cripples the idea of lazy loading reducing initial page load time.
  2. As the application complexity grows, it becomes impossible to perdict which company records you are going to need in the application.
  3. Did I mention it sounds silly?

Example#2

As per the example mentioned on the Backbone-Relational website, consider following situation:

var mozilla = Company.findOrCreate(1);

mozilla.on('add:employees', function( person ){
    person.on('change:city', function( city ){
        console.log('%s lives in %s', person.get('name'), city.get('name'));
    });
    person.fetchRelated('city');
});

mozilla.fetchRelated('employees');

Try running this example, you’ll never see a log.

Workaround

I didn’t have any more juices left in my brain to debug the problem behind, but it worked when I tweaked it a bit like this:

var mozilla = Company.findOrCreate(1);

mozilla.on('add:employees', function( person ){
    person.on('change:city', function( city ){
        console.log('%s lives in %s', person.get('name'), city.get('name'));
    });
});

mozilla.fetchRelated('employees');

setTimeout(function(){
    person.fetchRelated('city');
}, 5000);

Of course it is an ugly workaround, but is there a sane way for the purpose?

Conclusion

I have many more examples in my source code to go on and on, but for a start, let’s be content with two. The crux of the examples above is that Backbone-Relational lacks a uniform interface/contract for accessing relational entities. It is quite possible that I am missing some point that is a per-requisite for properly using the tool, but my humble Googling abilities couldn’t enlighten me with any. If you know the proper way to solve the problems above, please share. They would definitely help me and a lot of other folks. Otherwise I guess this is an opportunity for creation of a new relational framework.

Umar Ashfaq

Umar Ashfaq is a full-stack web developer. His core strengths are Java (Spring, Hibernate stack) and JavaScript (both client-side and server-side). Follow him on twitter @umarashfaq87

More Posts

Follow Me:
TwitterFacebookLinkedIn

Related posts:

  1. Backbone.LayoutManager: How to prepend a view?
  2. Object Oriented Programming with JavaScript

4 Comments

  • 1
    John
    April 17, 2013 - 10:57 am | Permalink

    good post Umar, I had about the same experience as the one you described here with Backbone-relational.

    I found it very frustrating to learn and eventually after figuring it out, I realized it wasn’t even capable of what I needed.

    At least it has documentation now though!

  • 2
    August 20, 2013 - 12:42 pm | Permalink

    Umar :

    Maybe you want to take Backbone-Associations out for a spin. [Disclaimer : I am the author] It originally started out to be a lightweight, faster and memory-effecient option to Backbone-Relational and shares a similar API. But, more importantly, it provides correct eventing semantics in a nested scenario.

    More details @ http://dhruvaray.github.io/backbone-associations/

    Regards,
    Dhruva Ray

  • 4
    scythargon
    March 2, 2014 - 9:51 am | Permalink

    Umar, thanks for this post, so what do you recommend about related models? It is not covered anyhow in your next post “The BackboneJS Way of Thinking”.

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    *

    Wrap your code in [sourcecode language="java"][/sourcecode] tag (you can replace "java" with language you are using).