perfect-di
v1.0.2
Published
Simple alternative to many angular like DI tools
Downloads
8
Maintainers
Readme
perfect-di
DI made simple.
Definitions
Service - a piece of code or data that is useful for the application that other services can use (depend on). Provider - definition of a service dependencies and the exact way it should be created (provided). Module - a group of interdependent services (providers).
Philosophy
This package was created with the following ideas and requirements in mind:
- simple and transparent
- module described in one place - ideally a single JSON
- can be used without typescript - no decorators metadata tricks
- no (web server) domain specific features
- reusable modules that can be isolated and published on npm
- no DI engine specific code in services
- no binding to specific classes outside of the module - module doesn't import anything outside its directory
- hierarchical modules
- modules don't have to be complete - they can depend on services provided by the parent module
- parent module can use services provided by child modules (submodules)
- imports/exports between modules should be stated explicitly
- desired features
- lazy initialization of services
- cyclic dependencies detection
- one service with same dependencies used in multiple modules should be initialized once
- (not implemented) module deinitialization
Usage
Example 1 - Four Services in One Module
All you need to get started is to define a Module
object and call initModule
on it.
import { initModule, Module } from 'perfect-di'
import { UserService } from './services/user.service'
import { MongodbService } from './services/mongodb.service'
import { ApiService } from './services/api.service'
const MainModule: Module = {
providers: {
'configSvc': { // data as a service
init: () => {
apiPort: 8080,
dbUrl: 'mongodb://url',
},
},
'apiSvc': {
dependencies: ['configSvc', 'userSvc'],
init: async (configSvc, userSvc) => {
const api = new ApiService(userSvc)
await api.lister(configSvc.apiPort)
return api
},
},
'userSvc': {
dependencies: ['dbSvc'],
init: (dbSvc) => new UserService(dbSvc)
},
'dbSvc': {
dependencies: ['configSvc'],
init: async (configSvc) => {
const db = new MongodbService()
await db.connect(configSvc.dbUrl)
return db
},
},
},
}
async main() {
await initModule(MainModule)
}
main()
Example 2 - Submodules
There are multiple ways a provider can be defined in a module:
- local provider - defined locally through the
init
function, - internal import - imported from submodule via
importFrom: 'SubmoduleName'
, - external import - imported from parent module via
importFrom: null
.
In the last case parent module can rename external imports of its submodule through imports: { 'svcNameInSubmodule': 'svcNameInParent' }
syntax.
# config.module.ts
const ConfigModule: Module = {
providers: {
'configSvc': {
doExport: true, // make this provider usable by parent
init: () => ConfigService,
},
},
}
# api.module.ts
const ApiModule: Module = {
providers: {
'configSvc': { importFrom: null }, // import this provider from parent
'apiSvc': {
doExport: true,
dependencies: ['configSvc'],
init: (configSvc) => new ApiService(configSvc),
},
},
}
# main.module.ts
const MainModule: Module = {
providers: {
'configSvc': { importFrom: 'Config' },
'apiSvc': { importFrom: 'Api' },
},
submodules: {
'Config': { module: ConfigModule },
'Api': {
module: ApiModule
// imports: {
// 'configSvc': 'configSvc',
// },
},
}
}
Example 3 - Real Life Example
https://github.com/grabantot/data-net-backend/blob/master/src/modules/main.module.ts