Node.js: Inversion of Control

I consider myself a polyglot developer. In my (6 year) career I’ve been paid to code C++, Python, PHP, ActionScript, Ruby, plus the whole menagerie of tools and languages that make up front-end web development. I’ve dabbled in many more unpaid. Picking up a new language is pretty straight forward, picking up the idioms of that language (and in-depth knowledge of the frameworks/standard library of that language) is much harder. I write Java like a PHP dev and JavaScript like a Pythonista.

In my most recent project we’ve taken the decision to break apart our monolithic app into microservices (partly to address some scaling concerns, and partly as a premature optimisation for some future features which would have been more challenging under our previous architecture), and we’ve decided to build those microservices in Node.js.

JavaScript is a famously unopinionated language, this makes life hard as a dev. As a Python dev I could follow PEP-8, as a Java dev I used Spring, which forces you into a certain style of design; JavaScript has many ways of doing things with no clear one that’s best. I’ve written before about how I structure my JavaScript using AMDs, but back-end code has different behaviours, different requirements, so I started writing my JS in an approach I felt was tried and tested, as classes using Inversion of Control where the dependencies are passed into the constructor. My top-level JavaScript module behaved as the IoC container, instantiating objects and passing them around where they were needed. It made each module easy to test (I could just pass mocks into the constructor) although without a DI framework it did make that top-level module a bit messy.

We took the first iteration of this new service to code review and instantly the objections came — it didn’t feel like a typical Node.js application, you need to know too much about the object in order to instantiate it, JavaScript is tending towards the functional rather than class based (especially as most of the methods on my classes returned promises). So I rewrote it using the functional approach. A controller went from looking like this:

function StatusController(database) {
this._db = database;
}
StatusController.prototype.get = function(request, response) {
response.setHeader('Content-Type', 'text/plain');
this._db.ping().then(function() {
response.end('OK');
}).catch(function(err) {
response.status(500).end('NOT OK');
});
};
module.exports = StatusController;

To this:

var db = require('./redis_client');
function getStatus(request, response) {
response.setHeader('Content-Type', 'text/plain');
db.ping().then(function() {
response.end('OK');
}).catch(function(err) {
response.status(500).end('NOT OK');
});
};
module.exports.get = getStatus;

Basically the same, but less clutter in our top level JavaScript file (which sets up our routes). But part of me is still unhappy — we now encapsulate certain things into modules which return preconfigured classes (the redis_client file simply provides a Redis client configured appropriately, whereas before our IoC container would instantiate an object with the right config and pass that round). This feels too much like a singleton, which we’re all taught to be bad. Testing isn’t really that much harder though — although you can’t monkey-patch directly, utilities like proxyquire make it easy to inject your mocks in. Which makes me think, with a tool like proxyquire, does Node’s ‘require’ command just become an example of a service locator?

I’ve not read much about applying those kind of design patterns (things like IoC, etc) and software engineering techniques to Node, maybe there’s a whole body of text out there that I’ve missed, or maybe there’s a cultural resistance associated with things like these that’s associated with Java or enterprise development. Or perhaps Node development is just so new, these techniques just aren’t quite refined yet?

What does an idiomatic Node.JS application look like, which way do you code your apps, am I going down completely the wrong path as I venture into this?