Reputation: 54025
TLDR - which is more idiomatic in NodeJS (ES2015) and why?
Export classes, instantiate later:
foo.js:
export default class Foo {
}
bar.js:
export default class Bar {
constructor(foo) { this.foo = foo; }
}
app.js:
import Foo from './foo';
import Bar from './bar';
new Bar(new Foo());
Export instances - effectively make them all singletons:
foo.js:
class Foo {
}
export default new Foo();
bar.js:
import foo from './foo';
class Bar {
}
export default new Bar();
app.js:
import bar from './bar';
I'm coming from the Java world. Over there, DI frameworks usually create some kind of "app context" - they create instances of classes and wire the objects together, putting them all in a big bag from which you can get whatever you need.
There is a huge difference between a static "code import" (import com.acme.FooService
, which only imports the definition of the class) vs. getting an instance that you can call etc.
What is the most common, reasonable way to do this in NodeJS? The "export instances" option seems dirty to me, it feels like the piece of infrastructure that is responsible for loading code also becomes responsible for instantiating objects and wiring them together. Or maybe it is a false dichotomy and that's actually the way to go with Node?
Upvotes: 2
Views: 343
Reputation: 11
tl;dr: There's no reason why you couldn't apply Dependency Injection patterns or export classes in Node.js.
The principal reason you doubt to export classes—even if they have a singleton lifetime scope in your application—is the hacky, dynamic nature of JavaScript.
Of course, you can actually do quite a lot without resorting to the Dependency Injection pattern.
For example, as you mentioned, you can export instances of your classes thus making them all singletons. The module exported in that way doesn't even have to be an instance of any class. Consider the following code snippets:
export class Foo {
foo() {
}
bar() {
}
}
export default new Foo();
versus:
export function foo() {
}
export function bar() {
}
Thus, this is a quite frequent way in JavaScript ecosystem. It's okay in all senses if there is no internal module state involved. Say, a library which public interface consists of a bunch of functions (like jquery, lodash, etc). But there is still the issue that some classes (Bar
in your example) is responsible for fetching its own dependencies (Foo
).
You can also use some hacks like proxyquire. It's the common way people solve the testing problem in JavaScript. With such libraries you can intercept calls to require
(don't sure about support for ES6 module syntax like import
, though) and provide your own mocks/stubs for testing purposes.
However, as an application becomes more complex and grows larger, DI can help the maintainability of your source code.
I think that the main problem of applying DI practices in JS is immaturity. None of the current DI solutions in JavaScript are really popular at the moment, so not so many guidelines, no tutorials, no infrastructure. Moreover, many libraries in JavaScript claiming to be a DI Containers are either polluting the modules with ugly annotations or actually implementing the Service Locator anti-pattern or anything.
So, I think, the most reasonable way is to:
Of course, as with anything related to software development, choosing between DI or exporting singletons depends on your software's complexity and your requirements.
Upvotes: 1