Open patran opened 5 years ago
I'm also interested in the very same question. @patran did you find some clean solution to this in the meantime?
Running into this same issue and realizing that I'm effectively using the container as a Service Locator in my persistence layer.
Basic use case is that I have a facade object that implements an IPersistenceLayer (boundary of the application) but according to SRP the logic of each query is located in a separate class, and then located, via .get<T>
in my facade.
This is pretty textbook service locator, but it seems better to me than my facade directly import
ing each query implementation class.
I'd love a way to just inject all of my queries and then filter to the one that implements a particular interface, but that doesn't seem possible at runtime due to the lack of types at runtime, right?
Same issue here i'm wandering how to implement this case the right way. I translate a C# service that produce a report each reports are some kind of plugins (addins).
So after looking after a kind of plugin system on node I found inversify witch is really elegant and fine for my need.
Reports that I have inherit a base class that have the main function to process the report, each report could overwrites some methods and must implement some abstract (could also declare delegate to have some hooks).
So current app I wrote in Typescript knows what symbol to load and as we can't do some dynamic @inject i'm using toFactory that do the container.get inside to attach correct report.
According to best practice the only time you call the container get is for loading the root element and for the rest we should let @inject decorator do the work.
Is someone could point us in the right direction or confirm that we must use the get ?
Thanks
Based on what and where do you determine which one gets injected? I'll call the parameter that determines it the "tag" and for the sake of the explanation make it a string that's either "abc" or "def".
Depending on that, you could either inject a factory with the signature (tag: 'abc'|'def') => MyFooService
. This is appropriate if you only have the tag available at the point of use.
You can also use hierarchical containers; i.e. in your main container, you leave MyFooService
unbound. Then in whichever layer the "tag" is available - say your facades, you create a child container of your root container. (Tip: bind a () => Container
factory in your root container that does this, and inject it into your facades.) Then you bind MyFooService
to whichever variant is correct in the child container, and .get()
the service you're delegating the actual work to as one does in facades.
Mind that the latter will probably only work if your services are stateless and registered as transient or request scoped. You can't resolve a short-lived service from a longer-lived one without using a factory.
@john-landgrave - There's nothing stopping you from adding your own type identifier to the query object, and you can then use multiInject
.
Possibly excessive code example:
import {Container, inject, multiInject, injectable} from 'inversify';
type QueryVariant = 'foo'|'bar';
interface IQuery<TVariant extends QueryVariant = QueryVariant> {
variant: TVariant;
run(): void;
}
const IQuery = Symbol('IQuery');
interface IFooQuery extends IQuery<'foo'> {
}
const IFooQuery = Symbol('IFooQuery');
@injectable()
class FooQuery implements IFooQuery {
variant = 'foo' as const
run() {
console.log('foo')
}
}
interface IBarQuery extends IQuery<'bar'> {
}
const IBarQuery = Symbol('IBarQuery');
@injectable()
class BarQuery implements IBarQuery {
variant = 'bar' as const;
run() {
console.log('bar');
}
}
@injectable()
class Service {
constructor(@multiInject(IQuery) private _queries: IQuery[]) { }
run(variant: QueryVariant) {
for (const q of this._queries.filter(({variant: v}) => v === variant)) {
q.run();
}
}
}
const c = new Container();
c.bind(IFooQuery).to(FooQuery);
c.bind(IBarQuery).to(BarQuery);
c.bind(IQuery).toService(IFooQuery);
c.bind(IQuery).toService(IBarQuery);
c.bind(Service).toSelf();
c.get(Service).run('foo');
c.get(Service).run('bar');
We use inversify with express. We would like to get the right implementation @inject'ed based on parameters coming in from the HTTP request. For example,
The typescript interface MyFooService is implemented by MyFooServiceAbc and MyFooServiceCed
The controller MyFooController would have MyFooSevice @inject'ed. However, we would like to select, at run-time and be able to choose whether MyFooServiceAbc or MyFooServiceCed should be @inject'ed.
What would be an elegant approach?
Thank-you!