Dependency Injection in JavaScript

Dependency Injection (or DI, which is how I will refer to it from now on) is one of the foundations of good Object Oriented Programming (OOP) practice. There are many DI frameworks which abstract the fundamental idea, but does DI work in Javascript and do these frameworks help Javascript developers?

What is Dependency Injection

And a little bit of history…

“Dependency Injection” is a 25-dollar term for a 5-cent concept.” — James Shore

Dependency Injection’ is a loaded term and can mean different things to different types of programmers.
DI, essentially, is a software design pattern that deals with how components get hold of their dependencies.
There are only three ways a component (object or function) can get a hold of its dependencies:

  1. The component can create the dependency, typically using the newoperator.
  2. The component can look up the dependency, by referring to a global variable.
  3. The component can have the dependency passed to it where it is needed.

The first two options hard-code the dependency to the component when creating or looking up the dependency — this is not the most favourable solution as it makes it virtually impossible to modify the dependencies. This is a problem in tests, where it is often advantageous to provide mock dependencies for test isolation.
The third option is the most workable, since it removes the responsibility of locating the dependency from the component. The dependency is simply “handed” to the component.

In 1994, Robert C.Martin wrote the paper “The Dependency Inversion Principle”. This became to be known as the IoC/DI principle that influenced the DI frameworks, especially in Java.

DI was used so classes in Java would not be tightly coupled together, for example:

import java.util.logging.Logger;
public class MyClass {
  private final static ILogger logger;
  public MyClass(ILogger logger) {
    this.logger = logger;
    // write an info log message
    logger.info("This is a log message.")
  }
}

In the above overused example, the logger is passed into the MyClass constructor rather than it being constructed by MyClass.
In classic Java, ‘functions’ have to be contained in a Class as methods and you must instantiate a new object based on the class before you can use the method.
The only way to decouple a dependency between objects is to instantiate the dependency outside of the dependent object and pass it into the constructor of that object.

An “IoC” container or a “Service Locator” has the responsibility of “finding” and instantiating these dependencies.
(Note: There are other ways to pass in dependencies other than in the constructor: Field injection, Setter injection, Passing injector as a dependency, Making injector a global singleton.
These alternative ways to inject dependencies seem to be frowned upon and should not be used.)

Another benefit of DI is using Mock Objects in testing scenarios. The dependency can be switched out for a mocked version so you are only testing the function/method and not its dependencies.
DI has gained support from the Test Driven Development community as it makes running tests easier. Here is a trivial example in JavaScript:

function SaveItem(item, dataStore) {
  const db = dataStore;
  db.insert(item);
};

We mock our dependency:

function mockDb() {
return {
insert: () => assert(true);
}
};

And the actual test:

function SaveItemShouldCallInsertOnDb = function() {
const testDb = new mockDb();
const someTestItem = { name: “test item” };
SaveItem(someTestItem, testDb);
};

Implementations of DI in JavaScript

Angular’s take on DI and IOC, which is very similar to how most other language’s DI frameworks are implemented –from the Angular 1 documentation:

class SomeClass {
  constructor(greeter) {
    this.greeter = greeter;
  }
  doSomething(name) {
    this.greeter(name);
  }
}

In the above example SomeClass is not concerned with creating or locating the greeter dependency, it is simply handed the greeter when it is instantiated.
This is desirable, but it puts the responsibility of getting hold of the dependency on the code that constructs SomeClass.
To manage the responsibility of dependency creation, each Angular application has an injector. The injector is a service locator that is responsible for construction and lookup of dependencies.
This is acceptable in an OOP world where removing the ‘burden’ of ‘newing’ classes is beneficial.
The downside is that the dependencies are just strings in Angular 1 and mis-typing (and even uglifying code) breaks the DI system.

Angular 2 solves this problem at compile time, using TypeScriptInterfaces.

Examples of other similar JavaScript DI frameworks:

BottleJS: https://github.com/young-steveo/bottlejs
InversifyJS: http://inversify.io/
WireJS: https://github.com/cujojs/wire
Inverted: http://philmander.com/inverted/
Mocktail: https://www.npmjs.com/package/mocktail (DI in unit tests)

When to use Dependency Injection

“Just like any pattern it can become an anti-pattern when overused, and used incorrectly. If you are never going to be injecting a different dependency why are you using a dependency injector?”

Dependency injection is effective in these situations:

  1. You need to inject configuration data into one or more components.
  2. You need to inject the same dependency into multiple components.
  3. You need to inject different implementations of the same dependency.
  4. You need to inject the same implementation in different configurations.
  5. You need some of the services provided by the container.

Dependency injection is not effective if:

  • You will never need a different implementation.
  • You will never need a different configuration.

If you know you will never change the implementation or configuration of some dependency, there is no benefit in using dependency injection.

Does Dependency Injection work?

…and does it benefit JS programmers?

We need to define the terms and separate the premise behind DI from the “DI design pattern” and the “DI frameworks”.
DI frameworks are helpful in implementing the DI design pattern if you come from an OOP background and you continue to use OOP in JavaScript, although, in my opinion, the language itself can solve the problems that Dependency Injection frameworks try to solve.
Even though the DI premise is a sound one from a software engineering perspective, DI frameworks are not needed in languages that support higher-order functions as first class objects as you can just pass the dependency into the function and use it.

Alternative methods of decoupling code

Higher order functions

Higher order functions can accept functions as parameters or a function as a return value…

function foo(bar, func) {
  return func(bar);
}

Above, “func” is a function and a dependency “injected” via parameters to the “foo” function.

Module system

A languages module system can decouple code by creating small independent units of work which can be imported into other modules composing a whole system…

In ES6:

import foo from ‘Foo’;
export default function(bar) {
  return foo(bar);
}

Above, “Foo” is imported as the name “foo” and is used within the module to return its result when called with the “bar” argument. This function is also exported as a module to be consumed elsewhere.

Currying and composing functions

Currying is where a function only consumes one argument and returns a function to consume the remaining arguments later (if more than one parameter is supplied)…

In PHP:

function curried($foo) {
    return function ($bar) use ($foo) {
        return $bar + $foo;
    }
}
const add10 = curried(10);
add10(5); // returns 15
add10(8); // returns 18

Composition is a technique where small pure functions, with a single responsibility, are joined together to create a new function which has the functionality of the composed functions…

In ES6:

// func uppercase…
// func scoldName…
const shoutyTellOff = compose(uppercase, scoldName);
shoutyTellOff(‘roland’); // returns ‘STOP IT, ROLAND!’

Conclusions

While Dependency Injection frameworks are worthwhile in Static Languages, such as Java, it is not in dynamic languages such as JavaScript (and arguably PHP) and even less so in functional languages, such as Clojure or Haskell.

If your language supports higher-order functions (and if it supports currying, even better) and you have a functional mindset there is no reason to use DI frameworks.

Dependency Injection, the design pattern, in the case of a static class-based language, is a good pattern to implement and the underlying premise of DI (to pass in dependencies) is fundamental to good software engineering.

DI, the design pattern, is used to circumvent the deficiencies of the language — which is what design patterns intrinsically are.

If your language supports the feature you need “out of the box” then there is no need to implement a Design Pattern, but that’s a different blog post for another time…

References

https://docs.angularjs.org/guide/di
http://www.martinfowler.com/articles/injection.html
https://blog.risingstack.com/dependency-injection-in-node-js/
https://yauritux.wordpress.com/2011/04/03/history-of-dependency-injection-di/
http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
http://mikehadlow.blogspot.co.uk/2010/03/functional-dependency-injection.html
http://stackoverflow.com/questions/15336655/javascript-ioc-di-frameworks