@ teqfw / di

Some love to ride bicycles and some love to reinvent them. I am one of those who reinvent bicycles to ride them. A couple of years ago I already wrote on Habr about this " bicycle " of mine - a DI container for JavaScript. The subsequent discussion of the principles of operation of DI containers and their difference from the " Service Locator " quite strongly advanced me in understanding the operation of my own " bike " and resulted not only in a number of articles on Habré ( one , two , three , four ), but also in a significant completion of the " bike " itself.





Under the cut - a description of the work of the DI container ( @ teqfw / di ) as of the current moment. Limitations: the container is written in pure JavaScript (ES2015 +), works only with ES2015 + code, decorated in ES modules with the extension *.mjs



. Advantages: allows you to load and use the same modules both in the browser and in nodejs applications without additional transpilation.





DI Container Basics

A typical call to an abstract container of objects looks something like this:





const obj = container.get(id);
      
      



The sequence of actions of the container:





  1. Determine by id what kind of object the caller wants to get.





  2. Check the container for the presence of the requested object, if the object is in the container, return it.





  3. If the object needs to be created, then use the id to determine where the file with the object's source code is located and download the sources.





  4. ( ).





  5. .





  6. .





  7. ( ).





  8. .





— 5 , , .





ES-

, ES- — .., DI- ES-.





const obj = {name: 'Simple Object'};
class Clazz {
    constructor(spec) {
        this.name = 'instance from class constructor';
    }
}
function Factory(spec) {
    return {name: 'instance from factory'};
}
export {
    obj as ObjTmpl,
    Clazz as default,
    Factory,
}
      
      



, ES- (, ):





import Def from './es6.mjs';
import {Factory} from './es6.mjs';
import {ObjTmpl} from './es6.mjs';

const spec = {}; // empty specification
const instClass = new Def(spec);
const instFact = Factory(spec);
const instTmpl = Object.assign(ObjTmpl, {});
      
      



, DI-, ES-, :





  • ;





  • ;





— , , ( ) , DI- :





constructor(spec) {
    const dep = spec['depId'];
}
...
await container.get('dep1');
      
      



:





import Container from '@teqfw/di';

const container = new Container();
container.set('dep1', {name: 'first'});
container.set('dep2', {name: 'second'});

const obj = await container.get('dep1');
      
      



ES-, (, -).





@teqfw/di



:





  • : (connection



    , conf



    , i18n



    , …);





  • : (EsModuleId



    );





container.set(id, obj)



, ( ).





@teqfw/di



ES- , Module



(. “” “Javascript: ”).





, - . @teqfw/di



#



:





  • EsModuleId



    : ;





  • EsModuleId#ExportName



    : ExportName



    EsModuleId



    ;





  • EsModuleId#default



    EsModuleId#



    : default



    EsModuleId



    ;





@teqfw/di



:





class Clazz {
    constructor(spec) {}
}

function Factory(spec) {}
      
      



, (), — ( )? @teqfw/di



$



:





  • EsModuleId#ExportName



    : (, ) ExportName



    EsModuleId



    .





  • EsModuleId#ExportName$



    : , ( ), ExportName



    EsModuleId



    .





default



- ES- :





  • EsModuleId#default$







  • EsModuleId#$







  • EsModuleId$







Singleton

( ), . "" -$$



:





  • EsModuleId$



    EsModuleId#ExportName$



    : ( ) , .





  • EsModuleId$$



    EsModuleId#ExportName$$



    : .





, singleton’ — . DI- global-. - , , DI — .





@teqfw/di



:





let id1 = 'named'; // named singleton been added manually
let id2 = 'EsModId'; // ES module
let id3 = 'EsModId#'; // default export of ES module
let id4 = 'EsModId#name'; // named export of ES module
let id5 = 'EsModId$'; // singleton from default export
let id6 = 'EsModId$$'; // new instance from default export
let id7 = 'EsModId#name$'; // singleton from named export
let id8 = 'EsModId#name$$'; // new instance from named export
      
      



, , , ( ). @teqfw/di



:





constructor(spec) {
    const named = spec['namedSingleton'];
    const inst = spec['EsModId#name$$'];
    const single = spec['EsModId$'];
}
      
      



@teqfw/di



, , , , . , , .





ES-, . @teqfw/di



:





container.addSourceMapping('EsModId', './relative/path');
container.addSourceMapping('EsModId', '/absolute/path', true);
      
      



( ) , , — nodejs-. , .





, , , namespace’, _



:





EsModId_PathTo_Mod => /absolute/path/PathTo/Mod.mjs
      
      



The @ teqfw / di DI container allows you to use both ES modules themselves as dependencies, as well as individual elements from the export of ES modules, as well as create new instances of objects or use one single object for the entire application. Moreover, the same code can be used both in browsers and in nodejs applications.





Typical code for an ES module used in @teqfw/di



:





export default class Mod {
    constructor(spec) {
        const Clazz = spec['Lib_Dep#'];
        const single = spec['Lib_Dep$'];
        const inst = spec['Lib_Dep$$'];
        // ...
    }
}
      
      



The usual import



one can also be used in the code, but in this case the code loses the ability to be used simultaneously in browsers and in nodejs applications, since the browser import format is not compatible with the nodejs format.








All Articles