Last active
September 30, 2021 15:43
-
-
Save domenic/5753428 to your computer and use it in GitHub Desktop.
How DI container config should work (in JS)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
// Domenic needs a Tweeter | |
function Domenic(tweeter) { | |
this.tweeter = tweeter; | |
} | |
Domenic.inject = ["tweeter"]; | |
Domenic.prototype.doSomethingCool = function () { | |
return this.tweeter.tweet("Did something cool!"); | |
}; | |
module.exports = Domenic; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
// Merick also needs a tweeter | |
function Merrick(tweeter) { | |
this.tweeter = tweeter; | |
} | |
Merrick.inject = ["tweeter"]; | |
Merrick.prototype.doSomethingAwesome = function () { | |
return this.tweeter.tweet("Did something awesome!"); | |
}; | |
module.exports = Merrick; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
function App(domenic, merrick) { | |
this.domenic = domenic; | |
this.merrick = merrick; | |
} | |
App.inject = ["domenic", "merrick"]; | |
App.prototype.run = function () { | |
this.domenic.doSomethingCool().done(); | |
this.merrick.doSomethingAwesome().done(); | |
}; | |
module.exports = App; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var diContainer = require("di-container"); | |
// Declaratively wire up dependencies. Note that while Domenic and Merrick both need the "tweeter" | |
// abstraction, we can choose *via configuration* to give them a different "tweeter" concretion. | |
// They are completely decoupled from this knowledge. | |
diContainer.config({ | |
"app": require("./3-App"), | |
"domenic": { | |
constructor: require("./1-Domenic"), | |
inject: { | |
"tweeter": require("./LolSpeakTweeter-not-shown") | |
} | |
}, | |
"merrick": { | |
constructor: require("./2-Merrick"), | |
inject: { | |
"tweeter": require("./LeetSpeekTweeter-not-shown") | |
} | |
} | |
}); | |
// Construct the entire object graph, using above declarative config. | |
// This is the *only* time you should ever use `diContainer.get`. | |
diContainer.get("app").run(); | |
// Your framework might use `diContainer.get` itself, e.g. for convention-based lookups. | |
// But you never should. | |
// Should Tweet: | |
// - "LOL I HAZ DID SOMETHING COOL LOL!" | |
// - "1 d1d s0m3th1ng 4w3s0m3!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
// You can also of course wire up the graph manually. | |
// That's still doing dependency injection. | |
var Domenic = require("./1-Domenic"); | |
var Merrick = require("./2-Merrick"); | |
var App = require("./3-App"); | |
var LolSpeakTweeter = require("./LolSpeakTweeter-not-shown"); | |
var LeetSpeekTweeter = require("./LeetSpeekTweeter-not-shown"); | |
var app = new App( | |
new Domenic(new LolSpeakTweeter()), | |
new Merrick(new LeetSpeakTweeter()) | |
); | |
app.run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I really believe that if you're using CommonJS modules already (with Browserify for example) you don't really need a DI container.
In order to prove it, I have rewritten the Coffee Maker example found in this video https://www.youtube.com/watch?v=_OGGsf1ZXMs#t=121
https://github.com/royriojas/coffe-maker
Basically you can just use the
injectr
approach of provide a second parameter to therequire
function.This second parameter is the list of modules to be mocked when required inside the test. This means require is tampered in your testing environment.
So using the same file as the one you provided like:
In testing env
And that's it, now you can write your tests with ease.
I know for sure that DI can do way more things that just replace a mock during testing, but I would say that the vast majority of javascript projects just need to reuse the code during testing. So this will work.
Using this approach I was also able to switch implementations on runtime, that is a bit more complicated and involves playing with browserify to require/export modules, to make them available outside the bundle, but it is totally possible. So far I have not found a single feature provided by a DI container to make me thing we need one in Javascript.
I have provided a demo of how to do this with Karma and publish the code for be consumed in a browser.
I know also that ES6 modules are coming, I would rather prefer CommonJS format, but let's see what happens. It is good to have more options...