JavaScript MV*: Knockout & Durandal

I've been doing a lot of work with JavaScript MV* frameworks lately and I thought it would be a good idea to write down some of my experiences.  This isn't going to be about which framework is better, this is just my opinion about several frameworks based on my experiences with them.  I've got quite a bit of experience using Knockout on several projects, so I thought that would be a good place to start.

Knockout

This isn't an MV* framework in the fullest sense.  It's a really good data binding library, that also happens to render small HTML templates (really just snippets).  There are a lot of comparisons made between Knockout and MV* frameworks, but I think anyone that tries to make those comparisons either doesn't understand what Knockout is trying to accomplish or doesn't understand what the other frameworks were designed for.  I don't blame them though.  It took me quite some time to get my head around the complex landscape that is client-side MV* frameworks; for the longest time I thought Knockout versus Angular was a valid comparison, but now I know it's not.

In my opinion, Knockout has two use-cases that it's really good at:

  1. Adding a layer of rich client-side interactivity to a server-side MV* framework.
  2. Serving as the data binding component in a client-side MV* framework you assemble yourself.

In either case, Knockout is only one component of an MV* framework; it isn't a complete MV* framework on its own.  In the first scenario, I've successfully used it in the past to add rich client-side interactivity to a traditional ASP.NET application.  In the second scenario, you could build your own MV* framework using various specialized libraries (e.g. data binding, routing, etc.) or you could use a framework that someone else has assembled using the same methodology.  In this particular instance I'm referring to Durandal (which I'll talk about shortly).

If you decide to use Knockout for either scenario I would recommend a few things to get you started in the right direction:

  1. Don't mix your JavaScript and your HTML; keep them completely separate.  I've covered this specific topic before and the quick summary is to use the classBindingProvider plugin to keep your HTML and your logic separate.
  2. Spend some time thinking about how you should structure your JavaScript models and logic.  Knockout doesn't care how you structure your code, but if you don't come up with a standard structure it will quickly get out of hand (especially if several developers are working on it at the same time).
  3. If the models coming from your server are big and complicated, the default mapping plugin might not cut it.  I've had good experience with the viewModel mapping plugin and I'd recommend it to others if you have to deal with these more complex scenarios.
  4. If you're only targeting browsers that are ECMAScript 5 compliant (IE9+), then I'd recommend using the Knockout ES5 plugin.  This plugin makes use of ECMAScript 5 getter/setter pairs, which means that you don't have to remember all of the parentheses the Knockout traditionally demands.

Durandal

This is the framework that should be used when attempting to compare Knockout with various MV* frameworks.  Durandal is a fully-featured MV* framework that uses Knockout as its data binding mechanism, along with few other libraries and some custom code to make everything work well together.  If you're a big fan of Knockout or have a lot of experience with it, using this framework is an easy way to take your first step into the client-side MV* world.

I don't have a ton of experience with this framework compared to a lot of the others, but I did spend a full week building various demos to give this framework a fair chance.  Based on my limited experience, Durandal does one thing very well:

  1. It makes it very easy to create a client-side MV* application if you have experience with Knockout and the standard ASP.NET stack.

It's great for that and I found it very easy to use given that background; however, I believe Durandal has some weaknesses:

  1. It has a very small community compared to the other major players in this space.  This is fine for small applications, but when you need to build bigger applications that you need to support for years, a large community becomes a significant resource that must be considered.
  2. By definition, the framework is an assembly of several other libraries.  For some this could be an asset since you can easily switch-out components.  For me, if I'm reaching for a large framework like this to start with, I'd actually prefer something that was designed end-to-end with that intent.
  3. The project is maintained largely by a single developer.  Rob Eisenberg has done great work and I'm honestly impressed with everything he's done, but having a larger number of people backing the project and driving it forward gives me more confidence that it will be around for a long time (I know that's largely an illusion, but it still makes me feel better).
  4. There was recently a failed Kickstarter to fund the next phase of development.  I know that doesn't signal the death of the project, but it doesn't inspire confidence that this is something that the community will get behind for years to come.

Again, I think it's a great framework for what it's good at, but it just didn't match the criteria for the project I was evaluating it for.

JavaScript MV*: Library vs Framework

The landscape of JavaScript MV* frameworks is complicated for anyone just tuning in; when I first tried to get caught up on everything, it was certainly confusing and I had no idea where to start.  Once I had it all figured out, I then had the challenge of explaining it to everyone around me, which I couldn't articulate well until I saw this video of Tom Dale and Yehuda Katz.  After I saw that, everything I was trying to explain finally clicked and it was just a matter of plotting all of the frameworks on a graph:

library_vs_framework.png

The graph is less about each framework's exact position and more about illustrating an idea.  That idea is simple:

In order to build any large application you need to use a rigid, opinionated framework.

Now, I'm certainly not saying that if you want to build a large application you have to use something like Ember or Chaplin.  What I'm saying is that you have the choice between using a framework that already exists or you'll have to write your own by using one of the low-level libraries and then build up your own opinions on top of that.  However you slice it, you effectively end up at the same place before you're able to build a large-scale application.

I've read countless articles about developers using Backbone (or similar low-level library) and most of them sound like this:

"Backbone was rough at the start, but now it's the best!  It took us about a year to figure out all of the patterns and structure we wanted, but now that all of the developers are on the same page it's going pretty well."

I'm sorry, but did you say a year?  I'm not joking; I actually read an article where it took the team a full year before they didn't hate working with it.  What took so long?  That was how long it took them to figure out how to structure their code, and define their internal patterns and best practices in a way that made sense for all of the developers on the team.

Wow.

Sure that's Backbone though, and everyone knows that Backbone is only a library.  Well, I know lots of people love Angular and believe it's the best thing out there, but I really think it is midway between a library and a framework.  It gives you more tools and higher-level abstractions than something like Backbone or Knockout, but it completely abandons you on when it comes to application structure and best practices.  After watching one of the teams at Google talk about their experiences building a large Angular application, it just reinforced my view; a lot of what they talk about is their struggle to find a structured way to build the application.  That's right, Google is struggling to find structure with their own tool.

I'm not saying that any specific library or framework is good or bad.  All I'm saying is that you should pick the right tool for the right job.  You shouldn't waste your time figuring out best practices and code structure if there's already something out there that's close enough.  Of course, if you're only building a small application or there's only one developer, the benefit of the larger frameworks isn't as clear and it's more likely to just get in your way.  In the end, you should be spending your time solving problems unique to the application you're building and not fiddling around with the tooling underneath it.

Apply Business Logic Like CSS

Providing a rich client experience in the browser has been something that we (as a community) have been trying to do for as long as I can remember.  You don't have to look very hard to see the evolution of our attempts.  Over the course of a decade or so there have been several efforts to make this a reality: ActiveX controls, Java applets, Flash, and Silverlight are just the ones that I can name off the top of my head.  As support for ECMAScript 5 grows (and with the death of Internet Explorer 8 just around the corner), we're finally in an era where we can provide a rich client experience in the browser with nothing more than JavaScript.

This is certainly an exciting time, but just because the technology is available, that doesn't mean developer adoption is guaranteed to be high.  Why is this?  Surprisingly, most of the developers that I'm aware of are happy relying on the server-side technology they're so comfortable with; the shift to a thick-client in the browser means that a lot of developers will need to be pushed out of their comfort-zone.  I think this is an incredibly promising area, but many developers will need to be introduced to the concepts slowly in order to get buy in.

Admittedly, when I first tried to push more interactivity to the client I was a little lost.  In my latest project I decided that we'd try to layer in more client-side interactivity than we've done in the past, but I wanted to do it in a maintainable way.  I didn't want it to be bolted on at the end of the project with jQuery plugins; I wanted the interactivity to have first-class support in our application.  I also didn't want beautifully structured server-side code that does a hand-off to client-side code that's spaghetti.  After all, a rich client isn't worth it if it's a nightmare to maintain.

Over the last few years I've had my eye on Knockout and the ecosystem surrounding it.  Based on everything that I knew about it, it would be a good addition to Microsoft's standard MVC stack.  The problem with vanilla Knockout is that there isn't a good separation of concerns when it comes to your views and the logic to display them.  For instance, a standard Knockout view would have snippets JavaScript code throughout it like this:

<div data-bind="visible: myFunction() > myValue">
  Some Text
</div>

Obviously that's a simple example, but as you build more complex logic these inline JavaScript snippets can get out of hand.  Luckily there is a handy plugin that allows you to pull this logic out and apply it to your view like CSS tags:

<script>
  var bindings = {
    myClass: function(context, classes) {
      return {
        visible: this.myFunction() > this.myValue
      }
    }
  };
</script>

<div data-class="myClass">
  Some Text
</div>

I've left out the standard Knockout code to bind everything together, but hopefully you can see that this will allow you to completely separate your view from the logic behind it.  Just getting that separation is a huge benefit, but you can do something much more powerful with this basic construct.  With CSS you define small reusable classes and then combine them on your view to build a more complex style.  Just like CSS, you can begin to apply functional behavior the same way:

<script>
  var bindings = {
    show: function(context, classes) {
      return {
        visible: this.isMessageVisible()
      }
    },
    message: function(context, classes) {
      return {
        text: this.getMessage()
      }
    }
  };
</script>

<div data-class="show message">
  Message will be shown here.
</div>

This simple concept allows you to build tiny reusable pieces that define behavior.  You can then apply multiple behaviors to individual elements (or a hierarchy of elements) to compose more sophisticated behaviors.  In my latest web application I've built behavioral classes for tons of basic things: show, hide, valid, invalid, enable, disable, and other more complex classes that represent common behaviors in my business logic.

This method could certainly use some improvements, which I hope to work on in the future, but this is still miles ahead of where we were a few years ago.