Super simple dependency inversion/injection for javascript.
npm install --save cider-di
const service = require('./service');
const database = require('./database');
const cider = require('cider-di');
const injector = cider.createInjector({
service: cider.singleton(
({database}) => new service(database)
),
database: cider.singleton(
() => new database()
)
});
const my_service = injector.dependencies.service;
Cider uses named bindings to create a map of name to binding function. Each binding function is supplied 3 values.
- dependencies - an object where each key corresponds to provided value.
- providers - an object where each key corresponds to a provider.
- context - a javascript object that can be used for providers to communicate with each other.
module
function
Returns injector
Takes 0 or more functions which act as module definitions, each module is passed an argument bind
which is used to declare dependencies.
binding
binding
Returns binding
Given a binding function ensures that the function is only called once for each injector instance that it's used from.
my_service: cider.singleton(
({dependency}) => new service(dependency)
)
value
any
Returns binding
Given a value create a binding that will provide that value.
should_run: cider.instance(false)
name
string
Returns binding
Given a name creates a binding that provides the value bound to that name. This acts as a way to alias bindings. Note that tags won't be transferred.
database: cider.alias('postgres')
tags
string[],- binding
binding
Returns binding
Given a set of tags and a binding, returns a binding with the given set of tags appended to the tags on the given binding.
stripe_processor: cider.tagged(
['payment_processor'],
cider.singleton(
() => new Stripe()
)
)
- [
name
string]binding
A module is a standard javascript object with a name mapped to a binding.
module.exports = {
database: () => new Database(),
service: cider.singleton(
({database}) => new Service(database)
)
}
tags
optional string[]
Returns any
A binding is given 3 values that can be used to get dependencies, and retuns the value of the dependency.
some_binding: ({database},{query}, context) => {
if (context.should_run_query) {
return database.execute(query());
}
return null;
}
- [
name
string] any
Map of binding name to value;
- [
name
string]provider
Map of binding name to provider;
Injectors hold all the dependency mappings.
const db = injector.dependencies.database;
tags
string[]
Returns any
A function that when called supplies the value as a result of invoking the corresponding binding. The tags (if any) that were defined by the corresponding binding will be available on the provider
While cider borrowers some terminology from popular Java DI frameworks like Guice it's designed from the ground up to take advantage of javascript features. Primarily this takes the form of using object destructuring in function arguments rather than attempting to provide some form of function annotation.
The dependencies
parameter that is passed to a binding uses property getters to lazily resolve the dependency chain. Cider does simple cycle detection whenever a dependency is being resolved, this prevents cases of circular dependencies.
The providers
parameter contains the same keys as dependencies
however each value is a function. The result of invoking the function is the same as getting the value of a property on dependencies
. A Provider should be used whenever possible to lazily initialize a dependency, doing so allows for what would otherwise be unacceptable circular references.