Migrating IoC from AngularJS to BottleJS

Adopting the $inject syntax for specifying dependencies made moving from AngularJS to BottleJS trivial.

Recently I was moving some client-side services to a NodeJS server-side process. On the client they were wired into the AngularJS dependency injection framework, but on the server we were using BottleJS to provide dependency injection.

The generally recommended method to specify dependencies when configuring a service in both AngularJS and BottleJS is the inline array syntax. This method has the weakness that the dependency must be specified when configuring the container, which makes it very easy for the service constructor and container config to drift and get out of sync.

The alternate $inject property not only has the advantage of keeping the necessary dependency information right beside the service definition, making it easier to maintain, but also meant I could use it directly in BottleJS with no change to the service.

Inline syntax... boo!

// Services
function Foo() {}
function Bar(foo) {} // `Bar` depends on a `Foo` instance...

// Configure container
const bottle = new Bottle();
bottle.service('Foo', Foo);
bottle.service('Bar', Bar, 'Foo'); // ... which you must remember to specify in the container config

// Access service instance
bottle.container.Foo; // Singleton constructed by running the `Foo` constructor
bottle.container.Bar; // Had `Foo` instance injected into `Bar` constructor

$inject syntax... hurrah!

Using the $inject property on our service, and using the spread syntax for supplying the args, we can get nice consistent code:

// Services, now explicitly specify dependencies as special `$inject` array
function Foo() {}
Foo.$inject = [];

function Bar(foo) {}
Bar.$inject = ['Foo'];

// Configure container using the spread operator
const bottle = new Bottle();
bottle.service('Foo', Foo, ...Foo.$inject);
bottle.service('Bar', Bar, ...Bar.$inject);

// Access service instance (as per normal)
bottle.container.Foo; // Singleton constructed by running the `Foo` constructor
bottle.container.Bar; // Had `Foo` instance injected into `Bar` constructor
Published on: 18 Jan 2016