Imagine this code.
var someModule = require('pathToSomeModule');
someModule();
Here, we DEPEND not on the name, but on the path of that file. We are also using the SAME file every time.
Let's look at angular's way (for the client, i know, bear with me)
app.controller('someCtrl', function($scope) {
$scope.foo = 'bar';
});
I know client side js doesn't have file imports / exports, but it's the underlying concept that you should look at. Nowhere does this controller specify WHAT the $scope variable ACTUALLY is, it just knows that angular is giving it something CALLED $scope.
This is Inversion of Control
It is like saying, Don't call me, I'll call you
Now let's implement our original code with something like a service container (there are many different solutions to this, containers are not the only option)
// The only require statement
var container = require('pathToContainer')
var someModule = container.resolve('someModule');
someModule();
What did we accomplish here? Now, we only have to know ONE thing, the container (or whatever abstraction you choose). We have no idea what someModule
actually is, or where it's source file is, just that it's what we got from the container. The benefit of this is that, if we want to use a different implementation of someModule
, as long as it conforms to the same API as the orignal, we can just replace it ONE place in our ENTIRE app. The container. Now every module that calls someModule
will get the new implementation. The idea is that when you make a module, you define the api that you use to interact with it. If different implementations all conform to a single api (or you write an adapter that conforms to it) then you can swap out implementations like dirty underwear and your app will just WORK.
This approach is not for everyone, and some people hate lugging around the container.
My personal opinion, I would rather code to an interface ( a consistent api between implentations ) than code to a concrete implementation.
An actual example of Dependency Injection in node.js
// In a file, far, far, away
module.exports = function(dependencyA, dependencyB) {
dependencyA();
dependencyB();
}
// In another file, the `caller`
// This is where the actual, concrete implementation is stored
var depA = someConcreteImplementation;
var depB = someOtherConcreteImplementation;
var someModule = require('pathToSomeModule');
someModule(depA, depB);
The downside to this, is now the caller needs to know what your dependencies are. Some are comforted by this and like it, others believe it's a hassle.
I prefer this next approach, personally.
If you aren't using babel, or something that changes your functions behind the scenes, you can use this approach to get the angular-style parameter-parsing
http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript
Then you can parse the function you get from require
, and not use a container at all.