Skip to content

Declarative and simple IoC container for node.js applications

License

Notifications You must be signed in to change notification settings

ukitgroup/IoC-container

 
 

Repository files navigation

IoC-container

Declarative and simple IoC container for node.js applications

Travis Coverage Status Greenkeeper badge node npm

GitHub top language GitHub code size in bytes David David

license GitHub last commit semantic-release

Usage

import { IoCContainer } from '@ukitgroup/ioc';

class ServiceA {}

// First of all you have to define DI config:
const moduleManifest = {
 moduleName: 'module',
 providers: [
   {
	 isPublic: true,
	 token: 'serviceA',
	 useClass: ServiceA,
   },
 ],
};


// Then in your composition root just create container
const container = new IoCContainer();
container.loadManifests([moduleManifest]);
container.compile();

Provider types

You can provide by concrete realization with several ways:

class ServiceA {
  constructor(serviceB) {
   this.serviceB = serviceB;
  }
}

// Dependencies will be resolved and injected to instance automatically
const providerByClass = {
	token: 'serviceA',
	useClass: ServiceA,
	dependencies: ['ServiceB']
};

// You can provide constant value
const providerByValue = {
	token: 'connectionUri',
	useValue: 'mongodb://uri',
};

// You can provide by factory function
// Also resolved dependencies will be resolved automatically
const providerByFactory = {
	token: 'ServiceA',
	useFactory: (serviceB) => {
	  return new ServiceA(serviceB)
	},
	dependencies: ['ServiceB']
};

Public/Private scope

By default all providers define in private scope. If you want to use provider from another module you should define this provider as public

const moduleAManifest = {
  moduleName: 'moduleA',
  providers: [
	{
	  isPublic: true,
	  token: 'ServiceA',
	  useClass: ServiceA,
	},
  ],
};

const moduleBManifest = {
  moduleName: 'moduleB',
  providers: [
	{
	  isPublic: true,
	  token: 'ServiceC',
	  useClass: ServiceC,
	  dependencies: [
		// You should define which module this provider from
		['ServiceA', { fromModule: 'moduleA' }],
	  ],
	},
  ],
};

Class Factory (AutoFactory)

There are some cases when you want create instances in code i.e: Entities, Command pattern

It's very simple if your class doesn't have dependencies so you can create instance just in you code:

class Entity {
  constructor(name) {
    this.name = name;
  }
}

class Example {
  doSomething() {
    const a = new Entity('test');
    // ...
  }
}

But there are some cases when this class has dependencies and you can't just create object because you have to transmit resolved provider. Fortunately we are codding with Javascript and so we can follow js way :)

class Entity {
  constructor(service, name) {
    this.service = service;
    this.name = name;
  }
}

class Example {
  // You can inject Constructor with resolved dependencies
  constructor(Entity) {
    this.Entity = Entity;
  }
  
  doSomething() {
    // Now you still have to provide rest arguments
    // And all dependencies has already resolved
    const a = new this.Entity('test');
    // ...
  }
}

// Of course there are some config parameters to make this magic work :)
const moduleManifest = {
  moduleName: 'moduleB',
	providers: [
	  {
		isPublic: true,
		token: 'Entity',
		useClass: Entity,
		dependencies: [
		  // You should define which module this provider from
		  ['ServiceA', { autoFactory: true }],
		],
	  },
	  {
	    token: 'ServiceA',
	    // To make it clear you should define in both "autoFactory: true"
	    autoFactory: true,
	    useClass: ServiceA,
	  }
	],
}

Composition root

Usually you have composition root in you application. It's the only place where you can use container.get

const moduleManifest = {
 moduleName: 'moduleA',
 providers: [
   //... other providers
   {
	 isPublic: true,
	 token: 'httpPort',
	 useValue: 3000,
   },
 ],
};

const container = new IoCContainer();
container.loadManifests([moduleManifest]);
container.compile();

// You can get only public providers
const port = container.get('moduleA', 'httpPort');
http.listen(port);

Testing

We provide a comfortable way for testing

import { TestIoCContainer } from '@ukitgroup/ioc';
describe('Unit test', () => {
	const ctx = {}
	
	beforeEach(() => {
		ctx.container = TestIoCContainer.createTestModule([
		  //... providers definition with mocks
		])
		ctx.container.compile();
	});
	
	it('test case', () => {
	  // Here you can just get provider by token
	  // You don't have to transmit module name
	  const provider = ctx.container.get('providerToken');
	});
})

IDE will help you

if you don't use typescript, type hinting still can help you: example

Just be sure that you use type definitions only from ./public-interfaces. Internal types can be changed not in major release

More examples you can find in integration tests

TODO:

  • support decorators with typescript
  • TestIoCContainer for integration tests
  • Get public providers by tag

About

Declarative and simple IoC container for node.js applications

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 96.7%
  • JavaScript 3.3%