Dependency injection and IoC in Javascript

There has been some talk lately about DI and IoC in Javascript, and other dynamic languages such as Ruby and Python. A popular opinion seems to be that these concepts are obsolete in dynamic languages, mainly because you can alter an object at runtime. This certainly makes testing easier, but testability isn’t the main goal of DI and IoC, it’s more of a nice side effect, and a proof of that you’re doing it right.

DI is about pushing object creation and wiring as high up in the application as possible, which gives you one central location for the flow of logic.

Code components have dependencies, whether you’re using Java or Javascript. When your UI widget needs to make a server request to fetch some content, you depend on some code to carry that out. There are usually three approaches.

1. Use a global symbol, such as $.ajax in the world of jQuery.

2. Use a service locator, something like:

function updateCustomer(locator) {
  var server = locator.server();
  server.get("...");
}

Or having the locator being a global variable.

3. Only pass in what you need, like this:

function updateCustomer(server) {
  server.get("...");
}

The first option is the simplest. $.ajax is just a global function that anyone can use. This is also why it’s the most problematic, because if anyone can use it anywhere, you will end up with no idea of which parts of your application is making Ajax calls. You’re basically saying “use any tools you like, I don’t care” to your function.

The second alternative passes in a service locator instead, which is better because you have control over what functionality you expose through your locator. It’s like saying “here’s a bag of tools, you can’t use any other than these, but do what you want with any of these in the bag”.

The third is the most straight forward. It’s like saying “here’s a screwdriver, it’s what you’re gonna use”. Since we’re just exposing the server object, there is no risk that other features using other objects will be added to our function. That is a risk with the other two solutions, since they expose more functionality than needed. Another benefit is that it’s very easy to spot what dependencies your objects have, since they are declared in the constructor. With a locator or with globals, you might have dependencies hidden in large chunks of code that are easy to miss. You are forced to think very hard about how your application is layed out, because it becomes very easy to spot if an object is doing to much, if it requires too many dependencies. You also have to think about how to pass these dependencies around.

Dependency injection is hard

So if the third solution is so great, how come most people use the first two? Simply because it’s hard to achieve. Using a locator is simpler, since you just pack a bag with lots of things and pass it around. But if you use the third option, you have to keep track of who wants the different tools, and make sure that they get them. You also need to make sure that everyone can’t ask for anything.

In other languages, there are frameworks that help you with these things. Automating object creation in a language like Java or C# is somewhat simple, because you have type hints in the constructors. You can inspect a constructor and find out what it wants. But there is no way to do that in Javascript, since it doesn’t have types or type hints. Because of this, making a DI container in Javascript is bound to get messy, because you need to define construction rules for all objects. This is problematic because new objects can’t be added to your application without modifying the wiring code.

A shot at an automated container in Javascript

So we now basically know what we need. A container that is semi-smart and can assemble hierarchies of objects. It needs to be able to create objects by convention instead of configuration.
To achieve this, we have a few options. First off, we can cast a constructor to a string which gives us the whole method body and then parse out the argument variable names and use them to map to objects. This is a brittle and error prone idea, since someone might rename a variable and that might take down the entire application. Another option is to add a sort of type hints. Something like this:

function Server() {
  return {
    get: function(...) {}
  };
}

function updateCustomer(server) {
  server.get("...");
}
updateCustomer.dependencies = ["Server"];

It’s a little unorthodox and perhaps not so Javascripty, but it makes our object more self contained. Our function still isn’t grabbing Server direcly as with a Service Locator, it’s merely saying that it would like to get some implementation that behaves like Server does. But it isn’t very pretty, because the type hints are below the function body, which can be an issue with a more lengthy function. So here’s some syntactic sugar:

var updateCustomer = dependant(
  ["Server"],
  function(server) {
    server.get("...");
  }
);

The dependant function just maps the function together with the type hints, and it also gives you a simple way to check who has which dependencies.

So now it’s just a matter of inspecting the dependencies array of a constructor to be able to map it together. Here’s a proof of concept:

var dependant = function(dependencies, func) {
    func.dependencies = dependencies;
    return func;
}

var Container = function() {
    var namespaces;
    var instances;
    var constructors;

    return {
        _init: function() {
            namespaces = [];
            instances = {};
            constructors = {};

            return this;
        },
        addNamespace: function(namespace) {
            namespaces.push(namespace);
        },
        get: function(klass) {
            if(!instances[klass]) {
                if(constructors[klass]) {
                    instances[klass] = constructors[klass]();
                } else {
                    instances[klass] = this.create(klass);
                };
            };
            return instances[klass];
        },
        create: function(klass) {
            for(var i = 0; i < namespaces.length; i++) {
                if(namespaces[i][klass] && typeof namespaces[i][klass] == "function") {
                    var constr = namespaces[i][klass];
                    if(constr.dependencies) {
                        var args = [];
                        for(var j = 0; j < constr.dependencies.length; j++) {
                            args.push(this.get(constr.dependencies[j]));
                        };
                        return constr.apply(namespaces[i], args);
                    } else if(constr.length == 0) {
                        return constr();
                    };
                };
            };
            throw "Cannot create '"+ klass +"' because there isn't a registered instance or \
                   constructor, and there was no way to auto wire it.";
        },
        registerConstructor: function(klass, creator) {
            constructors[klass] = creator;
        },
        registerInstance: function(klass, instance) {
            instances[klass] = instance;
        }
    }._init();
};

And some usage code:

var ns = {};
ns.Alerter = function() {
    return {
        alert: function() {
            alert("Mjello!");
        }
    }
}

ns.MyObject = dependant(
    ["Alerter"],
    function(alerter) {
        return {
            doStuff: function() {
                alerter.alert("Mjello!");
            }
        }
    }
);

var cn = Container();
cn.addNamespace(ns);

var o = cn.get("MyObject");
o.doStuff();

I haven’t used this type of DI in a Javascript application yet, so I can’t really say if it’s any good, but I get a good vibe from it so I’m planning on using it for a Javascript application that I’m about to create.

An added benefit here is that the dependencies array can contain anything, not just strings with function names. It could contain CSS selectors to let you decouple DOM access, and maybe even remove the tight coupling with a library such as jQuery or YUI. It could also contain an array of method names, something like:

ns.MyObject = dependant(
    [["alert"]],
    function(alerter) {
        return {
            doStuff: function() {
                alerter.alert("Mjello!");
            }
        }
    }
);

This says, “I need one object, and I need it to have an alert method”, which is basically a flirt with a structural type system that is a very good fit for Javascript.

Conclusion

Javascript is a very open language. You can choose to go OO or procedural, and you can even choose which type of OO you want to use, classical, prototypal or parasitic. It’s only natural then that there are also a number of different ways to manage dependencies in an application. So when you start a new project in Javascript, you have a smörgåsbord of ways to write code. There’s no wrong or right, there’s only what is appropriate for your application. But when you’ve decided which way to go, stick with it.


About this entry