@azera/container
v2.1.5
Published
Javascript dependecy injection container
Downloads
117
Readme
Azera Container
- Intro
- Property Injection
- Container-aware class
- Factory
- Tags
- Auto tagging
- Predefined Services and Parameters
- Type-based injection
- Async
Intro
Azera container is a dependency injection
service container for JavaScript
written in Typescript
.
First simple example :
import {Container} from "@azera/container";
let container = new Container;
container.set('logger', class Logger {
log(message) {}
})
let app = container.invoke(['logger', class App {
constructor(logger) {
logger.log('Initialize app');
}
}]);
Simple injection with decorators in Typescript :
import {Service, Container} from "@azera/container";
class Logger {
@Inject('loggerNS') namespace: string;
log(message) { console.log(`[${this.namespace}] ${message}`); }
}
@Inject([ Logger ]) class App {
constructor(private logger: Logger) {}
init() { this.logger.log('Initailize application'); }
}
let container = new Container();
// Set a parameter
container.setParameter('loggerNS', 'app');
// Create an instance from App
let app = container.invoke(App);
app.init();
// Console output : "[app] Initialize application"
Property injection
class App {
@Inject('logger') logger: Logger;
}
Simply !
Container-aware class
import {ContainerAware, Container} from "@azera/container";
@Service('logger') class Logger {
log(message) { console.log(message); }
}
class App extends ContainerAware() {
init() {
this.container.get('logger').log('Initialize app');
}
}
let container = new Container();
container.add(Logger);
let app = container.invoke(App);
app.init(); // output: Initialize app
Factory
You can also use factories to generate services, only add Factory to the end of function name :
import {Container} from "@azera/container";
class Logger {
log() {}
}
container.set('logger', function loggerFactory() {
return new Logger;
});
let logger: Logger = container.get('logger');
// Or
let logger = container.get<Logger>('logger');
Tags
Also you can define tag for services :
import {Container, Tag} from "@azera/container";
abstract class Command { }
@Tag('command') class RunCommand extends Command {}
@Tag('command') class HelpCommand extends Command {}
let container = new Container;
container.add(RunCommand, HelpCommand);
let commands: Command[] = container.getByTag<Command>('command');
// Or inject them
class ConsoleApp {
@Inject('$$command') commands: Command[];
}
Auto-Tagging
You can do tagging automatically :
import {Container} from "@azera/container";
abstract class Command { }
class RunCommand extends Command {}
class HelpCommand extends Command {}
container
.autoTag(Command, [ 'commands' ])
.add(RunCommand, HelpCommand);
class ConsoleApp {
// When property is array it will assumes as tagged an its named used as tag name
@Inject() commands: Command[];
// When when property name differs from tag name we can use $$[tagName] as service name to inject tags
@Inject('$$commands') commandsList: Command[];
}
let app = container.invoke(ConsoleApp);
Create custom auto tagging function
container.autoTag(definition => {
return definition.name.endsWith('Command') ? ['command'] : []
})
Predefined Services and Parameters
// services.ts
import {Inject} from "@azera/container";
export default {
app: class App {
@Inject('logger')
logger: Logger;
run() {
// Run logic
}
},
// You can also declare service with Definition schema
logger: {
service: class Logger {
constructor(private ns: string) {}
},
parameters: [ '$loggerNS' ]
}
}
// parameters.ts
export default {
loggerNS: 'app'
}
// index.ts
import {Container} from "@azera/container";
import Services from "./services";
import Parameters from "./parameters";
let container = new Container(Services, Parameters);
let app = container.get('app');
app.run();
Type-based injection
We can also emit service configuration and naming and use type-based injection.
// Logger.ts
export default class Logger {
log(message: string) {
console.log(message);
}
}
// App.ts
import Logger form './Logger.ts'
export default class App {
constructor(@Inject() public logger: Logger) {}
}
// index.ts
import App from './App.ts';
new Container()
.invoke(App)
.logger
.log('Hello World');
Async
@Service({
factory: async function connectionFactory() {
let connection = new Connection();
await connection.connect();
return connection;
}
})
class Connection {
name = "default";
async connect() { /** Connection logic **/ }
async execute(query: string) { /** Command Exection **/ }
}
class Model {
constructor(@Inject() connection: Connection) {}
}
async function run() {
let container = new Container();
let model = await container.invokeAsync(Model); // Model will resolve after conectionFactory() resolve
let result = model.connection.execute("SELECT * FROM User");
}
Scope
class Request {
constructor(public requestId: number) {}
}
class EntityManager {
}
const container = new Container();
container.add(EntityManager);
const scope1 = container.scope(`request-1`);
const scope2 = container.scope(`request-2`);
scope1.setAlias(Request, new Request(100));
scope2.setAlias(Request, new Request(200));
assert( scope1.invoke(Request).requestId == 100 ); // true
assert( scope2.invoke(Request).requestId == 200 ); // true
assert( scope1.invoke(EntityManger) == scope2.invoke(EntityManager) ); // true