Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given ES2015, dependency injection and library abstraction, what should my ideal module look like in 2016? [closed]

If not, for one thing, I would be all on board for writing all of my modules like

import A from './a.js';

var B = function(){
  //use A
};

export default B;

and then using a compiler to build that into some browser or server format.

My one issue with the above however is the explicit specification of ./a.js in the import.

I understand why the spec went this way1, to be in favour of static analysis. But there are two very practical reasons why baking in both a module's filename and its path are trouble.

  1. As already raised here, when recycling modules frequently from project to project, it's very likely you won't be able to maintain a consistent path to that resource in your project tree. Baking an import call like import myModule from './../../vendor/lib/dist/mod.js' into a module's code doesn't exactly feel future-proof to me.
  2. Besides the path itself, specifying the filename also ties you down. Something like this seems innocent enough:

    import $ from 'vendor/jquery.js'

    But what about the day when I want to use Zepto instead of jQuery? I've found abstraction, particularly around vendor libraries, to be immensely useful when dealing with large codebases, opinionated developers, and an ever-changing JavaScript ecosystem. I may want to import React as my component library today, but what about tomorrow? Moreover, what if I'm going to be using the same module on both the client and server, but I need different versions of a dependent library?

I insist on robust (but clear and consistent) abstraction in my teams. Often times, abstraction has taken the form of some kind of namespacing. I fantasize a bit about this:

//BAD: Bakes React into my component modules
import ComponentLib from './React.js';

//GOOD: Leaves me free to use any React-like library
import ComponentLib from 'vendor.lib.component';

Where vendor.lib.component, in a Java-like way, has been registered somewhere previously.

Note that unlike in this question, my aim is not to have dynamic control over my imports. I don't want run-time flexibility, I'd like build-time flexibility. I should be able to sub-out a dependent framework for another one, or for a mock, or for something that will work in a particular environment, without having to worry about what dependencies my modules are calling, or trying to duplicate some crazy directory tree for every build product I'm after.

Similar questions have led to the suggestion of a library that leverages the System specification, like SystemJS. You can then use something like jspm to introduce a module map to get abstraction. But the moment I do that, I'm writing all of my modules differently:

System.import('a', function(A){
  //use 'A'
});

Is that suddenly the future? If so, why don't I just keep using AMD? Why even bother with ES2015 modules and running transpilers if I'm just going to go back to using an asynchronous-looking loader API?

More eye-rolling, I don't see much or any mention of tackling a module loader API standard in the ES2017 spec.

(EDIT: Question revised to meet standards of a non-opinion-based answer)

Given all of the above, I'm asking the community -- how do I write a JavaScript module that (i) abides by the ES2015 standard, (ii) does not reference a dependent module by its filename or path, and (iii) does not rely on extensive intermediate tools/configuration that would make sharing the module with multiple teams prohibitive.

--

Note 1 As @zeroflagL noted in the comments, the spec doesn't explicitly state that a module should be specified as a path, just a string (see ModuleSpecifier - http://www.ecma-international.org/ecma-262/6.0/#table-41). However, there is also a clear instruction to account for circular references, implying some kind of static analysis (http://www.ecma-international.org/ecma-262/6.0/#sec-imports), with file paths seemingly being the reference context of choice to this point. So we can't blame the spec for being rigid here, rather the opposite. The onus may then be on the rest of us to develop more robust implementations of import/ModuleSpecifier that lead to a secondary standard.

like image 374
Dan Avatar asked Apr 30 '16 22:04

Dan


People also ask

Which library is used for dependency injection?

Enterprise Library is a good example of a library that really takes advantage of a dependency injection container without being hard coupled to one.

Is dependency injection an abstraction?

Dependency Injection (DI) is a technique that helps us achieve loose coupling between objects and their collaborators. In this post, we will use constructor based dependency injection, however, we are not going to use any DI container for now.


1 Answers

To me, this seems to be one of the biggest unaddressed issues of the JS community. There are no best practices around dependency management and dependency injection around (at least to my knowledge).

There is a long discussion on this thread: Do I need dependency injection in NodeJS, or how to deal with ...?, but most of the solutions seem to work for specific cases only, and require changing the way you write modules somehow. And most shocking is that many answers argue that you don't even need DI.

My own solution for the issue is this minimal DI framework, which lets you define modules once, and it will wire them up for you with proper dependencies.

like image 193
gafi Avatar answered Oct 08 '22 02:10

gafi