Modeling Explicit Workflow With Code, In JavaScript And Backbone Apps

A long time ago, in what seems to be a previous life at this point, I wrote a small blog post about modeling and creating an explicit return value from a dialog form in a Windows application. Fast forward a lifetime (3 years) and I’m finding that this knowledge and experience is resurfacing itself on almost a daily basis with my work in Backbone and Marionette. In fact, I’ve used almost this exact pattern that I first used in WinForms, with several Backbone applications now. But I found it to be rather limiting in how I was able to implement it, because of the language and runtime differences. What I’ve settled on, though, is giving me the same benefit of explicit modeling, semantics, and encapsulating workflow: events.

A Poorly Constructed Workflow

It seems to be common in the JavaScript applications world, to have very poorly defined and constructed workflow in applications. We take one object and build some functionality. Then when the next part of the app needs to fire up, we call the object that runs it directly from the first object. Then when the next part of the app is requested, we call that object from the second one. And we continue on down this path ad-infinitum, creating a mess of tightly coupled concerns, tightly coupled concepts, tightly coupled objects, and a brittle and fragile system that is dependent entirely on the implementation details to understand the higher level concept.

For example, might have a human resources application that allows you to add a new employee and select a manager for the employee. After entering a name and email address, we would show the form to select the manager. When the user clicks save, we create the employee. A crude, but all too common implementation of this workflow might look something like this:

EmployeeInfoForm = Backbone.View.extend({
  events: {
    "click .next": "nextClicked"
  },

  nextClicked: function(e){
    e.preventDefault();

    var data = {
      name: this.$(".name").val(),
      email: this.$(".email").val()
    };

    var employee = new Employee(data);

    this.selectManager(employee);
  },

  selectManager: function(employee){
    var view = new SelectManagerForm({
      model: employee
    });
    view.render();
    $(".wizard").show(view.el);
  },

  // ...
  render: function(){ ... }
  // ... etc
});

SelectManagerForm = Backbone.View.extend({
  events: {
    "click .save": "saveClicked"
  },

  saveClicked: function(e){
    e.preventDefault();

    var managerId = this.$(".manager").val();
    this.model.set({managerId: managerId});

    this.model.save();
    // do something to close the wizard and move on  
  },

  // ...
  render: function() { ... }
  // ... etc
});

Can you quickly and easily describe the workflow in this example? If you can, then you’re a better person than I am. I have to spend time looking at the implementation details of both views in order to see what’s going on and why. I have to piece together the bits from multiple places and form a more coherent high level overview in my head. It’s not easy for me to see what’s going on because every time I look at another part of the code, I have to put together the pieces again to make sure I am not breaking someone from the other parts.

Too Many Concerns

We’ve mixed two different concerns in to very few objects, and we’ve taken those concerns and split them apart in some rather un-natural ways at that.

The first concern is the high level workflow:

  1. enter employee info
  2. select manager
  3. create employee

The second concern is the implementation detail:

  1. Show the EmployeeInfoForm 
  2. Allow the user to enter a name and email address
  3. When “next” is clicked, gather the name and email address of the employee. 
  4. Then show the SelectManagerForm with a list of possible managers to select from. 
  5. When “save” is clicked, grab the selected manager
  6. Then take all of the employee information and create a new employee record on the server

And I haven’t even gone through any of the secondary and third level workflow in this. What happens when the user hits cancel on the first screen? Or on the second? What about invalid email address validation? If we start adding in all of those steps to the list of implementation details, this list of steps to follow is going to get out of hand very quickly.

By implementing both the high level workflow and the implementation detail in the views – the details and implementation – we’ve destroyed our ability to see the high level workflow at a glance. That will cause problems for us as developers, because we will forget some of those details when changing the system, and we will break things.

Modeling An Explicit Workflow In Code

What we want to do, instead, is get back to that high level workflow with fewer bullet points and very little text in each point. But we don’t want to have to dig through all of the implementation details in order to get to it. We want to see the high level workflow in our code, separated from the implementation details. This makes it easier to change the workflow and to change any specific implementation detail without having to rework the entire workflow.

Wouldn’t it be nice if we could write this code, for example:

orgChart = {

  addNewEmployee: function(){
    var employeeDetail = this.getEmployeeDetail();
    employeeDetail.on("complete", function(employee){

      var managerSelector = this.selectManager(employee);
      managerSelector.on("save", function(employee){
        employee.save();
      });

    });
  },

  // ...
}

In this pseudo-code example, we can more clearly see the high level workflow. When we complete the employee info, we move on to the selecting a manager. When that completes, we save the employee with the data that we had entered. It all looks very clean and simple. We could even add in some of the secondary and third level workflow without creating too much mess. And more importantly, we could get rid of some of the nested callbacks with better patterns and function separation.

But let’s see what this would really look like in code that we could execute:

orgChart = {

  addNewEmployee: function(){
    var employeeDetail = this.getEmployeeDetail();
    employeeDetail.on("complete", function(employee){

      var managerSelector = this.selectManager(employee);
      managerSelector.on("save", function(employee){
        employee.save();
      });

    });
  },

  // ...
}

Yeah – turns out this is code that we can actually run, and it can be implemented fairly easily with a couple of Backbone views and a model for the details:

orgChart = {

  addNewEmployee: function(){
    var that = this;

    var employeeDetail = this.getEmployeeDetail();
    employeeDetail.on("complete", function(employee){

      var managerSelector = that.selectManager(employee);
      managerSelector.on("save", function(employee){
        employee.save();
      });

    });
  },

  getEmployeeDetail: function(){
    var form = new EmployeeDetailForm();
    form.render();
    $("#wizard").html(form.el);
    return form;
  },

  selectManager: function(employee){
    var form = new SelectManagerForm({
      model: employee
    });
    form.render();
    $("#wizard").html(form.el);
    return form;
  }
}

// implementation details for EmployeeDetailForm go here

// implementation details for SelectManagerForm go here

// implementation details for Employee model go here

I’ve obviously omitted some of the details of the views and model, but you get the idea.

The Benefits

There are a number of benefits to writing code like this. It’s easy to see the high level workflow. We don’t have to worry about all of the implementation details for each of the views or the model when dealing with the workflow. We can change any of the individual view implementations when we need to, without affecting the rest of the workflow (as long as the view conforms to the protocol that the workflow defines). And there’s probably a handful of other benefits, as well. 

But the largest single benefit of all these, in my experience, is being able to see the workflow at a glance. 6 months from now – or if you’re like me, 6 hours from now – you won’t remember that you have to trace through 5 different Views and three different custom objects and models, in order to piece together the workflow that you spun together in the sample at the very top of this post. But if you have a workflow as simple as the one that we just saw, where the workflow is more explicit within a higher level method, separated from the implementation details… well, then you’re more likely to pick up the code and understand the workflow quickly. 

The Drawbacks

Everything has a price, right? But the price for this is fairly small. You will end up with a few more objects and a few more methods to keep track of. There’s a mild overhead associated with this in the world of browser based JavaScript, but that’s likely to be so small that you won’t notice.

The real cost, though, is that you’re going to have to learn new implementation patterns and styles of development in order to get this working, and that takes time. Sure, looking at an example like this is easy. But it’s a simple example and a simple implementation. When you get down to actually trying to write this style of code for yourself, in your project, with your 20 variations on the flow through the application, it will get more complicated, quickly. And there’s no simple answer for this complication in design, other than to say that you need to learn to break down the larger workflow in to smaller pieces that can look as simple as this one.

But It’s Worth It

In the end, in spite of potential drawbacks and learning curves, making an effort to explicitly model your workflow in your application is important. And it really doesn’t matter what language your writing your code in. I’ve shown these examples in JavaScript and Backbone because that’s what I’m using on a daily basis at this point. But I’ve been applying these same rules to C#/.NET, Ruby and other languages for years. The principles are the same, it’s just the implementation specifics that change.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in AntiPatterns, Backbone, Javascript, Principles and Patterns, Workflow. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • David Madden

    Interesting article.

    Have you come across this post: http://freshbrewedcode.com/jimcowart/2012/03/12/machina-js-finite-state-machines-in-javascript/

    Looks like there is a bit of crossover in what you are trying to achieve.

    • http://mutedsolutions.com Derick Bailey

      yeah, i’ve seen that a few times. i’m not a fan of configurable state machines like that. something about that pattern and style has always bothered me, and I invariably end up fighting the state machine framework instead of having it facilitate my workflow in a more controlled manner.

  • Wwwboy

    Nice work!
    Such articles help me and others to get new way of thinking and developing programms!Thanks alot!

  • Peter Rust

    Great article as always, Derick!

    It’s very similar in concept to “The Stepdown Rule”, from a book I highly recommend: Clean Code by Robert C. Martin (the chapter on Functions, section on “One Level of Abstraction per Function, Reading Code from Top to Bottom”)

    Except, of course, for the asynchronous part.

    Just playing around a little, one could imagine it written using node.js conventions (a callback as the final argument that takes an err object for error handling). The pseudo-return-value(s) of a closeable form/dialog would be provided to the callback (after the err object):

    getEmployeeDetail(function(err, employee) {
    selectManager(employee, function(err) {
            employee.save();
        });
    });

    • http://mutedsolutions.com Derick Bailey

      I really need to read Clean Code. I think I have it on my Kindle, but have never got around to it. :P

      And yeah, the asynchronous bit with the callbacks kind of drives me nuts some times. There’s a handful of things that can be done to reduce the nesting of callbacks, but sometimes that ends up reducing the readability and understandability of the code, for the sake of keeping it “clean”. It’s hard to find those balances.

  • Tyronemichael

    I needed this. I am constantly looking for patterns with regards to large scale applications and backbone.js. The premise of the simplicity and organization backbone provides is easily said than done. I definitely need to find a pattern that seprates logic into more readable and maintainable codebase. This definitely provides an idea so thank you!

  • Anonymous

    I think Node.js helps with this pattern a bit; I dislike seeing all the nested stuff so I end up separating things into smaller chunks, which makes the “big” methods much smaller and easy to read.

    For example, I was doing a screen scraping proof of concept and what I *could* have done was do a deeply nested monster that went through about 4 HTTP requests and processed the results, but I didn’t, I created separate handlers for each step of the process and just separated them out so I could reduce redundant code and add error handling in the process. It was a simple proof but just by doing it in Node forced me to restructure how I did it.

  • Dustin Boston

    Yay controllers in Backbone! Love it!

  • http://twitter.com/dagda1 dagda1

    I think Ember like bindings is one of the main things missing from backbone.  These keep the code loosely coupled and lean.  You do have the events object in Backbone but you have to write all the code to  create and trigger the event.  The bindings model in Ember is very rich.

  • bessington

    dig this approach – can you show an example of how you would trigger the ‘complete’ events for employee detail? tried to implement this but can’t seem to get the events working..

  • Mark

    Hi, how would you solve the back and forth logic which comes with a wizard. Putting the callbacks in a logical hierarchy makes a lot of sense to me but I dont know how to get the reverse logic of this.

    • http://mutedsolutions.com Derick Bailey

      Wizards require a bit more than this. I wrote a library for a ciient project recently that creates a wizard process, and have been thinking about making it open source. It has a pre-requisite that makes it a bit tricky to get up and running, though.

      The general idea is that the step definition and navigation has to be separated from the step implementation. It’s everything I talked about here, taken one step further in the abstraction.

  • http://twitter.com/OsoRojo07 OsoRojo

    Derick, first off, awesome blog and work on Marionette. You’ve done more to educate me via your posts, articles and videos (yes, I bought them) in just over a week than all of the internet in the previous 3 weeks combined doing BackboneJS research.

    What you seem to be describing here is something I think you later formalized as the Marionette.Controller object. I’m starting to understand the role of controllers as ‘arbiters’, ‘mediators’ or even ‘puppeteers’ (bad pun), but I get confused in how multiple controllers interact and how they interact with layouts. An example is better. Let’s say I have an app with a layout and regions of: header, main, nav and footer that are managed by a controller. I have a router holding that controller to respond to URL navigation, and the controller holds a reference to the layout and handles calling ‘show’ for the various regions. The controller can also respond to events that may be raised and respond to them by calling show, etc. as opposed to navigation via the URL. All is good here and I think this is what you advocate.

    Now what happens when my header is also complex and has 3 regions of it’s own? It doesn’t really message via the URL, so a router is inappropriate, but something still needs to call ‘show’ on the header regions right? It’s also inappropriate that the constituent views of the header would hold a reference to the overall layout and call show themselves. I think I need some higher level construct to coordinate the calling of show in that header, but the only controller I have is currently handling the overall app layout, not the header layout. Should that controller handle both? Do I need another controller that responds to events raised by the header views and calls show on the header layout to change them? What am I missing here?