Merrick Christensen's Avatar
I have been impressed with the urgency of doing. Knowing is not enough; we must apply. Being willing is not enough; we must do.Leonardo Davinci

MV* Frameworks and Libraries

2012-11-25

Update June 7, 2018 - Hall of Shame

This article is a Hall of Shamer™ because I've since moved to different paradigms completely such as Single State Atom, GraphQL & React. While MV* was a dream on the backend it really broke down for me on the client.

So, we just explored DOM Libraries in our last article, which do a whole world of good when it comes to abstracting browser bugs and leveling the playing field. Certainly tools like jQuery are a powerful weapon in our arsenal for conquering the JavaScript landscape. It can be so powerful, in fact, people can unknowingly abuse it. Turning innocent interfaces into crimes against animation, and bludgeoning the separation of concerns with a round house kick to the teeth of maintainability. Let me show you the problem as it unfolds on so many websites and applications today, then I will walk you through some of the solutions we have at our disposal.

The DOM! The Trap! The Humanity!

A lot of times in our application we have the need for more interesting and complex user interface components. Historically speaking we often called these "progressive enhancements", which in fact they often are. Sometimes they are less enhancements, but rather requirements for our user interface to make any sense. When you get a powerful tool like jQuery into your hands your inclination is to take a boring DOM structure and spice it up with a little jQuery romance. Let me show you what I mean...

$.fn.abuse

jQuery has a wonderful plugin system which allows you to add methods to the jQuery object to provide more functionality to your selected elements. The trouble is in the lack of discipline and structure developers employ to construct these plugins. Often times plugins will do so many things all in one giant callback. Traditional Object Oriented programming patterns are thrown to the dogs while developers struggle to take a document composed of HTML and make it something incredible like users are growing to expect.

Take for example this element, we really want to make it do something cool.

<div class="some-element" title="WTHeck Man?"><h2 class="title"></h2></div>

So we implement our plugin, whatever it may be.

(function ($) {
  $.fn.abuse = function () {
    // Set some markup from DOM stored state
    this.$(".title").html(this.attr("title"));

    // Callback from AJAX to see if the user should receive their highfive.
    var highFive = function (response) {
      if (response.highfive === true) {
        $("<div>High five holmes!</div>").appendTo(document.body);
      }
    };

    // Check the time...
    var time = Date.now();

    // Inform the server the time which informs us to highfive or not.
    $.ajax({
      url: "stupid/request",
      data: {
        time: time,
      },
      type: "POST",
      dataType: "json",
    }).then(highFive);
  };
})(jQuery);

Then we select our element and instantiate our plugin. If you are wondering how this works, $.fn is simply a pointer to jQuery's prototype. (If you don't know what that means read up on prototypal inheritance. This is a great article to start with.) And "this" is the element you are calling the plugin on.

$(".some-element").abuse();

This approach is riddled with problems. Look at all the different types of work we did in the context of our abuse plugin. What if we needed to change the way all of our AJAX responses get processed? Why would the view need to know anything about the server in the first place? Why does this plugin have the power to reach outside of it's own element? What if the server wanted to schedule the plugin to high five in a few seconds? Should we put all that logic in our plugin too?

As with all types of programming separating our concerns is fundamental to maintainability. This is as true in JavaScript as it is anywhere else, imagine if the next implementation of your web services included all of your database queries, logging, routing, cacheing, etc in a single class. Yikes!

Obviously the $.fn.abuse plugin could be refactored into smaller functions but that still isn't enough. Even the jQuery UI project has tools to help write robust and separated components beyond simple plugins... Look into the Widget Factory which provides an inheritance model and improved structure. It is a serious problem, that grows with your project. Eventually plugins are fighting over DOM elements and sharing state, your pre-javascript markup looks nothing like your inspect-able source. It's a tearful road travel.

This is not to say interacting with the document to create nifty new user interface components is bad, I am just trying to point out that we need more! We need a layer to interact with our data, one that can handle validation and syncing to the server. We need a view that is agnostic to concerns except presentation. We need a way to keep our views and our models in sync so we can stop the madness of trying synchronize them ourselves. I am having a difficult time articulating how and why a project grows unmaintainable when you construct everything using the DOM. If you've worked on a project of any scale odds are you're already nodding your head saying, "Oh man, I know about this spaghetti mess already. You are preaching to the choir son, how do I tame it?"

The Structure! The Convention! The Beauty!

If you haven't already please review some of the structural patterns Model-View-Controller and the work done by Addy Osmani. JavaScript MV* or Model-View-? frameworks provide much needed structure and convention to web application development.

This article is not to explain MVC or other structural patterns. It's not to condemn using jQuery or other DOM manipulation libraries either. I am simply proposing those libraries have a purpose and we should keep them focused on doing what they do best, DOM manipulation and browser normalization! It is simply to spread the good news, these patterns fit in the JavaScript landscape just like they do in C++, Java, Objective-C or any other language! All those great patterns and principles we learned building native UIs are applicable in building JavaScript UIs as well! We can take our abuse plugin an make some sense of it... This example leverages Backbone.js. Ultimately we should refactor the DOM construction to a template library, but we will save that for another article.

// Create a model to store our data and application logic
var State = Backbone.Model.extend({
  defaults: {
    time: Date.now(),
    highfive: false,
  },

  url: "stupid/request",
});

// Create a view to *declaratively* represent our state.
var SomeView = Backbone.View.extend({
  initialize: function () {
    this.model.on("change:highfive", this.highFive, this);
  },

  render: function () {
    this.$(".title").html(this.$el.attr("title"));
  },

  highFive: function () {
    $("<div>High five holmes!</div>").appendTo(document.body);
  },
});

// Instantiate our view.
new SomeView({
  el: ".some-element",
});

See how we are able to take this horrible example and refactor it so we have a separation of concerns? SomeView is now responsible for the DOM manipulation, and State is responsible for all of our data management. There are so many wins to this kind of approach. Keeping them in sync is a breeze, maintainability and testability go way up because our code is separated into focused pieces. Reusing and extending components becomes easy. When leveraging an approach like this, constructing all of your markup in a browser becomes feasible, even sensible. We can write and maintain complex user interfaces that provide desktop class interaction. We can reuse code in new ways because it isn't coupled to a particular DOM structure, we can connect our data with different views. We can do all sorts of great things you didn't think JavaScript could!

The JavaScript landscape has a robust collection of libraries for you to pick from. Almost all of these libraries build on top of existing DOM libraries like jQuery or work nicely with them. Nearly every one of these libraries provide some notion of a Model and a View. Though their concepts and approaches are different they all provide great ideas and tools towards writing more maintainable web applications.

A Summary of Libraries

  1. Backbone.js The most prolific MV* library for JavaScript, lightweight and un-opinionated. Provides a great starting point for little cost. There is a large ecosystem of frameworks built on top of Backbone.js:
  • MarionetteJS A composite application library for Backbone.js
  • Chaplin An application architecture for Backbone.js
  • Thorax An opinionated Backbone application framework.
  1. Ember.js An excellent full featured MVC framework for creating ambitious web applications.
  2. AngularJS An awesome structural framework from Google providing an end-to-end solution for building web applications. Beautiful dependency injection and testability.
  3. CanJS Similar to Backbone.js, formerly known as JavaScriptMVC.
  4. Knockout An MVVM framework for JavaScript. Used heavily by the C# community.
  5. Spine Similar to Backbone.js though leans closer towards MVC, primarily used with CoffeeScript.
  6. Batman.js A full stack framework from Shopify, lots of useful convention.
  7. Meteor A promising new platform to construct rich JavaScript applications. Not exactly an MV* framework but worth a mention.

A Note

There are a plethora of MV* framework for you to pick from. TodoMVC is a great project to help you select the right one.

Coming Soon

Next we will talk about template libraries, giving us the power to move all our application logic to client, by constructing our markup in the web browser. After MV* libraries its the next logical step, right?