Sunday, September 8, 2013

I saw Dan Hollenbeck speak at The JavaScript Austin Meetup Group on "Three Backbone.Marionette/Node.js Apps: Useful Patterns and Lessons Learned."

The talk was on Thursday night. In beginning his talk, Mr. Hollenbeck emphasized a trend towards having more and more logic live at the front end of an application as the JavaScript space blossoms. In Backbone.Marionette SPA applications, Models (Business Logic, SQL) and Controllers (API) live Server-side (Node code I suppose in Dan's scenario), but a bigger piece of the overall pie lies at the Browser-side:

  • Models, Collections (State, AJAX)
  • View Helpers (Cell Render)
  • Routers (URL Glue)
  • Controllers (App Glue)
  • Events (Object Eventing)
  • Specialized Views (UI Eventing, Data Eventing, please see this which Dan Hollenbeck also detailed regarding eventing)
  • App Modules (App Life-Cycle, App Initializers)
  • Layouts (View Composition)
  • Regions (View Management)
  • View Callbacks (UI Widgets, View Clean-Up)

One of the criticisms I've heard of Backbone is that it is abundant with boilerplate code. Dan asserted that boilerplate code is not a bad thing and it is healthy to write it. It keeps your head in the game and allows you to understand what is really going on. You don't want to abstract too much away. The folder structure for a Backbone app looks like: api, docs, models, node_modules, processing, public, routes, ssl, etc. In the public folder are folders for all, css, img, and js. The folders inside the js folder are broken up by function/resource based upon different parts of the domain. This is not how you, strictly speaking, have to keep the contents of the js folder, but rather this is Dan's recommendation for keeping things sane. In eventing, listen from short lived objects, such as view, to a long lived object, such as models, and not the other way around as the other way around may cause a memory leak. View are unaware of the context in which they are being invoked. There are four kinds:

  1. Marionette.ItemView ...for a single object or a collection
  2. Marionette.CollectionView ...allow one to iterate over a collection
  3. Marionette.CompositeView ...tree/leaves/branches
  4. Marionette.Layout ...this is a container that holds the other views, has "slots" wherein you may place other views and if you put a new item in a slot then the Layout view will do the work of disposing of the prior occupant (without causing a memory) leak for you

Please note that a Backbone.Marionette application is a one page application wherein the stateless web suddenly has state and ongoing life. Challenges and benefits come with this. Helpers are used for markup dress-up. Views defer to helpers. Akin to my recent dojo/dojox experience there is an "App" variable in Backbone implementations which is the central player.

var App = new Backbone.Marionette.Application({
   models: {},
   collections: {},
   views: {},
   routers: {},
   lang: {},
   helpers: {},
   layouts: {},
   events: _.clone(Backbone.Events)
});

 
 

Initializers come in many shapes, for example upon the triggering of a view:

//Define Event Handler
App.events.on('car:detail:show', function(car){
   var dialog = new App.views.carDialog({
      model: car
   });
   dialog.render();
});

 
 

Dan refered to data points three tiers deep within the App variable such as App.events.on as "namespaces!" Here is how we would use what we just put in this namespace.

//trigger event to show dialog
var model = new App.models.Car({});
App.events.trigger('car:detail:show', model);

 
 

Another initializer, the Marionette.AppInitializer is executed just once immediately after the application starts:

Add.addInitializer(function () {
   
//Define Event Handler
   App.events.on('car:detail:show', function(car){
      var dialog = new App.views.carDialog({
         model: car
      });
      dialog.render();
   });
});

 
 

A view will/may have an initializer:

App.views.CarDialog = new Backbone.Marionette.ItemView({
   initialize: function(options) {
      this.model = options.model;
      this.collection = options.collection;
      _.bindAll(this, 'onCancelClick', 'onSaveClick');
   },
   ui: {
      wheel: 'input',
      dashboard: 'fieldset#dashboard',
      button: 'button.delete'
   },
   onEventFnc: function(event){
      this.ui.dashboard.hide();
      var wheels = this.ui.wheels.val();
      this.ui.button.button({icons: {primary: 'ui-icon-plus'}}
   },
   template: 'script#car-detail',
   templateHelpers: {
      horsePowerRender: function(horsepower){
         return horsepower + ' HP';
      }
   },
   events: {
      'click span.ui-icon': 'iconClick'
   },
   collectionEvents: {
      'sync': 'render',
      'destroy': 'render',
      'deselected': 'onUnselectionEvent',
      'selected': 'onSelectionEvent',
      'error': 'error'
   },
   modelEvents: {
      'change:wheels': 'renderWheels'
   },
   
//etc...

 
 

Clearly, there is a lot more going on above than just the initializer. :) In closing, an example of a model:

App.models.Car = Backbone.Model.extend({
   urlRoot: '/api/cars',
   shiftTransmission: function(transText){
      var trans = 'U';
      switch(transText){
         case 'park': trans = 'P'; break;
         case 'drive': trans = 'D'; break;
         case 'reverse': trans = 'R'; break;
      }
      this.save({trans: trans}, {
         url: '/api/cars/' + this.get('id') + '/transmission',
         silent: true
      });
   },
   isTransmissionIn: function(transText){
      var gear = this.get('gear');
      switch(transText){
         case 'park': return (trans === 'P')? true : false;
         case 'drive': return (trans === 'D')? true : false;
         case 'reverse': return (trans === 'R')? true: false;
      }
      return false;
   }
});

 
 

Other tools mentioned in this talk:

  1. Underscore.js was being used in Dan's app, of course.
  2. SendGrid for sending emails, an outsourced service.
  3. Loggly for remote logging. If you have an application straddling multiple servers, it may be painful to find the right machine to remote desktop into in the name of checking logs. Use a third party tool for consolidation.
  4. Dan said he tried to work with "X.js" (this is what I heard) and found it painfully complicated. He likes Backbone.js because there is just one page of documentation to read and understand. I wonder if he meant Sencha Ext JS.
  5. The sponsor who bought our pizza was Tech Pines and they have a testing platform for CasperJS/PantomJS headless browser technologies.
  6. DigitalOcean is a cheapo cloud service.
  7. Ember.js used to be called Sproutcore.
  8. Google Chrome Developer Tools were heralded as much better than the Firefox Firebug of yore. One of the things you may do with the tools is monitor how much of a memory footprint a one page web application consumes.
  9. gmail has a 40 to 60 megabyte profile as a one page application. If they can be this big you can go big too. Dan said his apps spike up to around 13 to 26 megabytes of memory.
  10. Twitter Bootstrap and jQuery UI are two of the few and far between providers that Dan trusts for third party eye candy which will not cause memory leaks in one page applications. When evaluating a dress-up widget for if it is safe or not, look to see if it has a Destroy() function or better yet, look to see if it is something you can just rewrite in a safer shape. Most third party eye candy is not yet made to be safe in one page applications and is instead built with the assumption that the user is just going to change pages away at some point doing all "garbage collection" work needed. This oversight may lead to (in one page applications) memory leaks and zombies, pieces of the DOM which ideally should have gone away yet rear their ugly heads inappropriately to your dismay.
  11. Amazon Web Services (AWS) allow up to a terabyte of hosting space. With this may come:
    • RDS is a cloud database.
    • OpsWorks is Amazon's canned configuration PaaS (platform as a service) stuff.
    • S3 is for document storage.
    • SQS (Simple Service Queue) is for data jumping between servers. It makes sure nothing gets lost when a server crashes. Think NServiceBus.

No comments:

Post a Comment