For almost one year, 9elements assisted in developing a large JavaScript web application: Moviepilot.com is a place to discover upcoming movies, track them from rumor to release and share opinions with other movie buffs. The site let’s you connect easily with Facebook, Google or Twitter to subscribe to news channels or take part in polls and discussions.
While the backend uses a more classic technical stack with Ruby on Rails, MySQL and Neo4j, the frontend is a sizeable JavaScript application written in CoffeeScript, HAML and SASS. The actual user interface is build completely in JavaScript while the raw data is fetched from the server over a JSON API.
We chose the popular Backbone.js library as a basis. By design, Backbone is rather a minimal library than a complete application framework. However, huge JavaScript applications are built using Backbone – the examples include top-class apps from Groupon, LinkedIn and Diaspora. Backbone is a good start for smaller applicatons, but as the project grows you will need a sophisticated architecture on top of Backbone.
In the last couple of months, Backbone-based application frameworks were released, most notably Thorax by WalMart and Marionette by Derick Bailey. While developing moviepilot.com, we came to similar conclusions – unfortunately the mentioned frameworks were releasing long after we began working on moviepilot.com.
Chaplin, an Application Architecture based on Backbone.js
To promote the discussion on Backbone-driven applications, we have released the JavaScript core of moviepilot.com into the public. The project is called Chaplin. It’s completely free and open-source. Of course the name pays homage to Charlie Chaplin, the great silent film maker. You can take a peek into code on Github and read an in-depth essay on the application structure.
In the meantime, Chaplin grew from an example application architecture to a separate library which is ready for use. You might build upon it and adapt it to the needs of your specific application. The key features of Chaplin include:
- CoffeeScript classes wrapped in RequireJS (AMD) modules
- Cross-module communication using the Mediator and Publish/Subscribe patterns
- Introducing controllers to separate routing and business logic
- Application-wide view and state management
- Consistent memory management and proper object disposal
Chaplin ships with a demo application that allows you to log in via Facebook and browse your Facebook Likes. Of course this isn’t as complex as the stuff on moviepilot.com, but it shows how to build application modules upon the Chaplin structure.
Join the discussion
Chaplin aims to documents our experience on developing large JavaScript applications. It’s also a playground and testbed for new ideas. Since we’re constantly improving the architecture, your feedback is highly appreciated. Feel free to get in contact. You might also fork the code or create a new issue on Github.
I’d like to thank the team of Moviepilot.com as well as the people of 9elements for making Chaplin possible.
Peter Seliger hat kommentiert:
2012-02-25 02:38
hi molily,
at first glance (30 min code review) it looks promising. I definitely will dig myself deeper into the code in order to maybe steal some of the ideas/approaches of yours.
congratulations - petsel
asdf hat kommentiert:
2012-02-29 04:54
I looked into the FIRST code file (mediator.coffee), and, guess what: horrible!
1.) use some real annotated documentation system (like jsdoc for javascript) so you can generate proper documentation afterwards.
2.) comments are REALLY horribly inaccurate to put it nicely.
# The mediator is the objects all others modules use to
# communicate with each other.
# It implements the Publish/Subscribe pattern.
if communication is its sole purpose, why the frak is there a user (!) property and a seemingly randomly thrown in router (!). It's looks like having been glued together in a hurry, and/or without thinking about it. This "design" smells.
3.) retarded and redundant comments:
# Current user
mediator.user = null
doh! really? or
# The router
mediator.router = null
what I would expect for example would be:
/**
* @see some.Router
**/
mediator.router = null
and not debating here, that the mediator should not even have a user or router, if he is just in for communictation! There are a lot of those examples in this file.
4.) WRONG COMMENTS:
# Initialize an empty callback list (so we might seal the object)
mediator._callbacks = null
empty list? EMPTY LIST? this is an empty list []. null !== []
5.) and really the best one:
# Return mediator
mediator
it's one tiny step from
# return 3
1
molily hat kommentiert:
2012-02-29 15:52
Hey anonymous coward,
hilarious comment, very entertaining!
FYI, the code documentation is not an explanation of the architecture. In fact we have developed and refined this structure during almost one year of work on moviepilot.com. If you would like to understand why we chose this mediator approach and why the router and the user are shared objects on it, please read the elaborate README.md. Or you can just look into the files which use this router property and also offer some explanation.
Obviously you did not do that and cannot come up with a better idea on how to solve that specific problem. In case you have any, how about joining the discussion and creating an issue on Github instead of ranting here. If you don’t have anything worth to say about Chaplin apart from ridiculous remarks about commenting, please move along. Thanks!
Fadhli hat kommentiert:
2012-03-22 09:01
Looks like this has amazing potential. Will definitely check it out.