Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Patterns for Managing Large Scale Backbone Applications (benhowdle.im)
83 points by benhowdle89 on March 25, 2013 | hide | past | favorite | 28 comments


Ben -- since you're around, and it seems to be "struggle with getting started with X.js week" around these parts ... I figure I should ask you about this:

    > which is what causes problems for a lot of people 
    > when getting to grips with it (myself included).
What exactly did you have a hard time coming to grips with?


Right,

First of all, thanks for commenting on the thread! Also, I really hope it hasn't come across as though this post was "bandwagoning" on the "struggle with getting started with X.js week". The reason I LOVE Backbone is the fact is the fact that it's not opinionated on application architecture.

However, IMO no one (or not many) people build single page todo apps. People have real, multi-state and multi-screen apps that they're trying to build and I think that Backbone site/docs would really really helps people take their understanding of Backbone and architecture to the next level if an example was provided that went beyond the humble Todo app. DOM structure, Zombie events and View management (subviews, etc) are things that I feel are missing, not from Backbone itself, but that Backbone should/could provide as examples of using Backbone to deal with these real, use-case issues that arise.

I think this lack of documentation/examples acted as a catalyst for things like Marionette (hat tip Derick Bailey), but if patterns were outlined from the get-go on the Backbone site, people would be less inclined/feel the need to throw something like Marionette at it.

Hope this has come off as my humble opinion and not just "grumpy developer snark" - I love Backbone but I'd love to see it have an easier route for learning to build real world apps.


Jeremy, what I've seen most people struggle with (including myself in the beginning) is those first couple of hundred lines of code. How does everything fit together? How should I handle templates? How to I create nested models? How do I stop thinking "in jQuery" and start thinking "in Backbone"?

I guess the thing is that the current documentation works really well when you understand Backbone. Before you understand it, getting started is really difficult. That's why we see so many getting started guides all around — the problem of course being that most of them are barely just ok. Backbone should have a really, really good getting started guide, which (I think) should be more opinionated than Backbone itself — i.e. it shows one (really good) way of doing things (that works well when you scale up your application). Or at least link to one.

I've heard many say "look at the todo example", but I think that it's so far away from a real app that it's difficult to use as a starting point. It doesn't use ajax, it relies heavily on global state, it puts templates in the HTML, lots of global `$` stuff, no routing, etcetc. Ok-ish choices, but not a great example for those getting started. E.g. having templates in the DOM is ok when you have 5 views. When you have more it's problematic, especially for testing and for precompiling templates.

Backbone is great when you grok it, but it takes a lot of time to find your own patterns that works really well. (Marionette and Chaplin are awesome, but still, first when you have some basic understanding of Backbone itself)


>> How does everything fit together?

initialize models and controllers, then pass that data to newly initialized views. at any scale, this hardly differs.

>> How should I handle templates?

backbone is template agnostic so the answer is "however you'd like". because I use r.js, my preference is loading the templates as separate text modules and defining them on my view. when our production script is build, the templates are inlined into the javascript through r.js.

your qualms with the todo example (no AJAX, global variables, strange template handling) are things that are not at all pertinent to Backbone and are solved by other patterns. point being, Backbone has it's own model/view pattern, but you can look at it in isolation. your other concerns are things solved by patterns with no pertinent relation to Backbone.


I wasn't commenting on Backbone itself, I was commenting on getting started with Backbone. I've held 15+ workshops and presentations on Backbone and helped a lot of people getting started. These are the issues I have most often heard from them.


my point is that these questions seem either misdirected or simply answered. that is to say, it's not a framework. it's a set of classes. how you handle templates, how these classes fit together, etc is hardly a function of Backbone.

don't get me wrong. i've been there. i've had the same questions. but i think the answers i gave would have satisfied me when i was starting, had i had a mentor.


Catshirt, I actually would love to get feedback on this comment:

>> initialize models and controllers, then pass that data to newly initialized views. at any scale, this hardly differs.

Where do you find it makes the most sense to initialize models & controllers? I've done it in the router function but that feels really heavy and gets big.

Alternatively, since I use require.js I have used a view wrapper that initializes everything and then puts together the sub-views and models/collections as necessary. I like this approach because it feels more organized having it delegated out to my various view files.

Thanks in advance :)


router functions i think are sufficient. if size is a problem, multiple routers could help. different routers could also generalize instantiation for certain types of pages. if it gets more specific than that, i put it into the page view logic like you suggested. does this sound reasonable?

aside, but to my original point, you could use any router and you'd still have this problem. these type questions are probably best not answered by Backbone.

edit: no need to downvote. i am open to suggestions or criticism.


I don't want to waste this opportunity of telling you my thoughts, so I'm gonna let your question settle a litte. Will reply soon :)


I recommend anyone getting into building web apps (regardless of framework) to read Mixu's "Single page apps in depth" http://singlepageappbook.com/single-page.html

It's a short read, it's opinionated and will make you think, especially if you're a novice / intermediate programmer. In particular, the "Writing maintainable code" chapter.

Focusing on things like namespacing is not necessarily a bad thing, but it doesn't address the real goal - writing modular, testable, maintainable code. If your components know where other components are in the namespace and depend on them internally, the namespace hierarchy does nothing to help your design.

The main difficulty in designing complex web applications is managing dependencies between objects, just like any large object-oriented piece of software (assuming that is the paradigm you choose).


Oh, that backbone...

-- disappointed network engineer


Same here, though I'm not a network engineer.

Can we please, please say "backbone.js" when we're not talking about that "other" backbone (which has a much older claim on this name)?


You should get used to it.. More and more non-CS programmers are coming with "JS is everything" and without any other tips.


Nitpick: App and Models are singleton instances. Shouldn't they be lowercased ?


Truly nitpicking ;), you're probably right, but as a convention it makes sense for me and hopefully others!


I honestly don't feel like any of the "large scale (JS|Backbone) applications" posts really addresses the difficulties you run into when scaling JS applications. Most of these things are common sense (eg. "use namespaces", "make use of consistent naming conventions" etc.) and should hold for any kind of application development.

For me, one of the main problems when scaling the Backbone app I work with is decoupling View interactions. Now the usual recommendation you receive is using an event bus. In the link @analog just posted [1], Addy Osmani talks about using the Mediator pattern to achieve the decoupling, which certainly does help, but imho his example does not fully reflect the purpose of the Mediator pattern. His Mediator is barely more than just an event bus.

A Mediator can make use of an event bus, but an event bus is not automatically a mediator. It still forces the views to know about events triggered from other views and react to them. This still causes coupling, and even worse, it keeps the coupling in the views. The mediator should exist to take care of that coupling and pull it out of the views.

Let's take for example a checkout page. The checkout page has a view containing a form for address data. It also has a view that lets you pick the payment provider, a view that allows you to enter a coupon and a view that shows the order total.

Here are some of the required view-to-view interactions:

* If the user email changed, revalidate the coupon to make sure the "new users only" constraint applies and show the status

* If the payment type is changed to Paypal, add an additional charge to the order total and display it.

* If a coupon is entered, update the order total

This means that in many cases, views need to know exactly how the public event interfaces of the other views look, even if they communicate by an event bus and not by direct references and method calls. To make the application maintainable, we need to get rid of the knowledge about the other views' events as well.

This is where the Mediator comes into play. We make the mediator aware of the different event interfaces of the Views and have it decide which events to retrigger or which methods to call in what View. Instead of having the views subscribe to the other Views' events, the Mediator wires them up and encapsulates the View interactions. The Mediator now knows that the `change:email` event from the AddressForm triggers the `validateCoupon` method on the CouponView or the `validate:coupon` event to which the CouponView listens. The CouponView no longer needs to know about the existence of the `change:email` event.

Now the Views only need to specify their public (event) interfaces and the Mediator knows about how the different Views interact. This is a lot more scalable than the naive event bus approach.

I really wish there were more articles about scaling really big JS applications. In the end they will probably heavily borrow from GoF's Design Patterns book, but it always helps to see the patterns in action. I appreciate the author's effort, but I always feel disappointed when I read "Large Scale (Backbone|JS) Applications" and don't find advice to solve and real problems what come up with scaling "Large Scale (Backbone|JS) Applications".

[1] http://addyosmani.com/largescalejavascript/


The example you give is the kind of thing that Ember is really good at.

You would implement all those interrelationships with computed properties. Each computed property updates automatically if its dependencies change, and each view redraws automatically when the corresponding properties change. All that work of defining events and propagating them through the mediator is stuff that Ember does for you automatically, once you've declared what depends on what.

I'm one of the early adopters who built a rather big application in pre-1.0 Ember. It definitely pays off as the application grows larger.


the problem is a large JS application is no different from a large desktop application. You cant code your way out of it without States , Proxies , Commands , Mediators , etc ... wether they are blundled with the framework one uses or not. Or one writes "throw away" code , which is what most people do anyway.


a bit of a side track here, I've been seen a lot of blogs with this same layout, is it a blogging platform or just people copying around?

If it is a blogging platform, which one is it?


Are you thinking of https://svbtle.com/ ? Yeah mine looks similar but I assure you, it wasn't ripped at all!


Sorry Ben, I didn't mean to accuse you of anything. Sorry if it came out that way. And yeah, that's what I was looking for :) thanks!


The big piece missing from this is RequireJs as the author mentions. We should all know it's good practice not to use global variables so that should be reason enough to use it in itself.

As an example an app we built had a conflict between a jQuery plugin that the frontend devs had included, and a library that we had added with require. Having the conflict only show up in one place rather than the whole app certainly made it quicker to diagnose.

Addy Osmani's tutorials should be required reading if you're just starting out. [1] [2]

The only downside that we saw was that you will have to provide AMD versions of each library you use if you want to use jQuery from a CDN.

As well as being good practice it just feels really nice to have everything you're using declared at the top of the file you're in. The sugared require syntax helps with this. [3]

[1] https://github.com/addyosmani/backbone-fundamentals

[2] http://addyosmani.com/largescalejavascript

[3] http://requirejs.org/docs/whyamd.html#sugar


If you find RequireJS a bit too intrusive you could check out a dependency manager I wrote: https://github.com/judofyr/dep.js

It's tiny (411 bytes minified; 268 bytes gzipped) and only handles dependency management. It's excellent if you just want to avoid worrying about the order you concatenate your files, but it's also possible write a basic RequireJS-style script-loader on top of it.


I have found it is convenient to use RequireJS's shim features to load libraries that use AMD rather than looking for AMD versions. For example, we use MathJax off their CDN and they do not provide an AMD version.


Are you using the r.js optimiser though? We definitely couldn't use a local non-AMD version with other CDN libraries when using the optimiser to build our js out for production.


AMD is a pain to use , and make the code really ugly. It has some compatibility issues with some older (but widely deployed ) mobile browsers and doesnt really fix javascript shortcomings. You can still have global variables into a module anyway. And a lot of libraries dont even support AMD , why should they ?


It's a pain because some libs support AMD and some don't, but it's the least bad solution that I know of. If there's a better option I'm all ears.

Of course you can add a global variable in a module if you want to, but 1. why would you? and 2. If this happens by accident it should be picked up by a linter.

Which mobile browsers have issues? Would be good to know.


browserify




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: