# Factories
Diosaur allows you to declare factories to create your services. These make it possible to register anything as a service in Diosaur.
# Class factories
All class factories must implement the IFactory
interface as well as
be decorated with @Factory
.
class MyService {
constructor(@Parameter('param') private param: string) {}
}
@Factory(MyService, { /* other settings like tag, scope... */ })
class MyFactory implements IFactory {
resolve(params: any[]) {
// Using reflect to build your service
return Reflect.construct(MyService, params);
// Building it manually
return new MyService(...params);
}
}
The factory decorator takes a mandatory argument which is the service identifier. It can take a second optional argument to change service settings.
# Resolve method
Every factory class must implement the resolve
method. As you can see,
it provides a single argument, params: any[]
, which actually contains
all parameters and services that should be injected through constructor.
In the case of the upper example, params
would be something like ['value of param']
.
Diosaur automatically builds this array with correct order of arguments,
it's your job to inject it in your service.
WARNING
Avoid services which have Diosaur and non-diosaur injected constructor arguments, Diosaur only knows about the decorated ones, the order will be wrong if there's some he's not aware of.
# Async class factories
You can also set async class factories easily by setting the resolve method
as async
or returning a Promise.
@Factory(MyService, { /* other settings like tag, scope... */ })
class MyFactory implements IFactory {
async resolve(params: any[]) {
return new MyService(...params);
}
}
# anonymous factories
You can register some anonymous factories with the register
method to avoid creating a class.
The first argument is the service identifier, the second is either a function that will generate
the service or the service itself, the third is the service configuration.
import { register, getContainer } from 'diosaur';
register('a', 'a'); // register a primitive as service
register('b', () => 'b', { scope: 'renewable', tag: 'B' }); // register a primitive as service and custom config
register('c', { key: 'value' }); // register an object
register('d', new Something()); // Simple object creation
register('e', (args: any[]) => new SomethingElse(...args)); // object creation with injection
// These two do the same
register('a', 'a'); // Internally converted to () => 'a'
register('a', () => 'a');
# Async anonymous factories
You can also register async anonymous factories easily using registerAsync
.
import{ registerAsync } from 'diosaur';
registerAsync('d', new Promise((resolve) => {
setTimeout(() => {
resolve('D');
}, 100);
}));
# A note on async factories
All services registered through async factories must be scoped as singleton
. That's
because once registered, calling the await getContainer()
will actually create those
services and keep them in the service registry for future usage.
If we didn't do it, when a service would require a dependency on a service registered
through an async factory we couldn't resolve it, but would resolve a Promise. That could work,
but then how would we inject constructor dependencies, and you would have to await
a promise
to receive your service.
That's why all async services must be singleton
scoped.
# Registering services after initialization
It is possible to register dynamic services and parameters after retrieving the container.
import { getContainer, register, refreshContainer } from 'diosaur';
let container = await getContainer();
register('a', () => 'a');
container.get('a'); // Will throw an error
container = await refreshContainer();
container.get('a'); // a
WARNING
Note that refreshing the container will completely wipe any existing scope along with all cached instance