-
-
Notifications
You must be signed in to change notification settings - Fork 308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ Adds dependency injection system #1461
✨ Adds dependency injection system #1461
Conversation
@palle-k Can you show an example of how you would register providers in |
@rmtuckerphx sure! app.configure({
providers: [
...
]
}); This would replace all the providers declared in Should we merge the list of providers (giving providers provided to |
@palle-k What I want to make sure is that I can handle the following stages/environments:
|
@palle-k Any thoughts on adding an Event Middleware for injection? I would like to see how injection was resolved in the Jovo Debugger similar to routes |
I think we should merge instead of replace. |
For dev and integration, I would be using ServiceADev and staging and prod, the real ServiceA both of which implements ServiceAInterface. |
@palle-k Just want to say a huge THANK YOU for working on this. |
@rmtuckerphx dependency overrides via app.configure have been implemented. |
I'm interested how you would expect a dependency injection middleware to look like. |
@palle-k Maybe there are 2 event middlewares: each individual resolve and injection complete |
@rmtuckerphx I've added a middleware that outputs the whole dependency tree that contains information about the token being resolved, the resolved value and any arguments that have been included in the construction of the resolved value. Let me know if this is sufficient for you. |
34f99f1
to
c392f3b
Compare
Hi @palle-k, this looks very very interesting! I've never used Nest.js so a couple of questions came to my mind:
|
For each time that a component/output class is instantiated. Instances are not cached in any way. In Nest.js there is the option to cache dependencies and I guess this can be added as a future expansion to this system.
Yes, this was also important for me! If you look at the code, you can see that the dependency injector declares a provider for the Jovo instance. So if you declare an injectable with the type Jovo (or with
|
Thanks for the explanation, that's perfect! |
Thank you this feature is so cool
El vie., 11 nov. 2022 20:17, Gianluca Acerbis ***@***.***>
escribió:
… Thanks for the explanation, that's perfect!
—
Reply to this email directly, view it on GitHub
<#1461 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AMTE3JCVOVS2WG6A27WLL6TWH2LTRANCNFSM6AAAAAAR4TXRBY>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
This is such a cool feature, thanks a lot @palle-k 🎉 Can't wait to merge this! What do you folks think about grouping all services under a this.$services.orderService.performOrder(); This would not necessarily add a lot of value, but be consistent with the naming of other Jovo properties. |
Can you go more into detail about this?
|
I meant this one! Same functionality as the one you implemented, the only difference would be how the services are accessed by the user. |
This seems a little cumbersome for me:
const APP_CONFIG_TOKEN = Symbol("app_config");
app.configure({
providers: [
{
provide: APP_CONFIG_TOKEN,
useValue: loadConfig()
}
]
});
So I would actually vote against this. |
@jankoenig Will this feature be merged soon? |
We're planning on merging this on Monday |
So excited for this. |
# Conflicts: # framework/src/ComponentTreeNode.ts # framework/src/index.ts
@palle-k How would you use interfaces with dependency injection?
|
Interfaces do not exist in JavaScript, just in TypeScript. If you use an interface, it will appear as If you want to inject a class that follows a given interface, you have 2 options:
const ORDER_SERVICE_TOKEN = Symbol("OrderService");
@Injectable()
class SomeClass {
constructor(
@Inject(ORDER_SERVICE_TOKEN) readonly orderService: IOrderService
) {}
}
app.configure({ providers: [{
provide: ORDER_SERVICE_TOKEN,
useClass: OrderServiceImpl
}]}); The following types can be dependency tokens:
|
@palle-k Perfect thanks! I also found this article that helped: https://jasonwhite.xyz/posts/2020/10/20/nestjs-dependency-injection-decoupling-services-with-interfaces/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @palle-k 🎉 Great feature!
Is it safe to store data (like a mongodb bulk) in an injected service as a member? I mean, the service instance is not shared for multiple users, right? So each instance of the same service is different. |
@jrglg Yes. If you use a If you use a |
All clear! thanks |
Hello again, I'm not sure if this mine or not so I'm not opening an issue yet. It works nicely on local, with express, but fails at runtime in Lambda with these errors, at every injected service (actually not injected):
My Global LAUNCH handler is so simple and just call a dumb method. I tried putting providers at every level. Please, anyone ever tried it in AWS Lambda? Thank you |
@jrglg looks like this is a limitation of esbuild - see this issue. TL;DR: esbuild does not implement a type system and is therefore unable to emit decorator metadata. There are some options how to continue:
|
Proposed Changes
This PR adds a dependency injection system that works similar to the system in Nest.js.
Quick Demo:
Providers can be marked as Injectable. Doing so registers them in the metadata storage and allows them to be constructed automatically:
Providers are then registered in the app:
And we can then use these providers in components and output classes.
Providers can also be defined in a more granular way, which is especially desirable during testing:
Dependency injection can be traced using middlewares:
Note on @Injectable
Looking at the codebase it may not be immediately obvious why
@Injectable
is needed. However, it is essential to the construction of providers, as adding it to a provider causes metadata about the parameter types of the annotated class to be emitted. Without it, the Injection system would be unable to provide constructor parameters for the injectable class. Also, configuration options can be added in the future if desired.Types of Changes
Note on breaking changes: This PR changes the constructor signature for
BaseComponent
andBaseOutput
.By changing from
options?: TYPE
tooptions: undefined | TYPE
, subsequent parameters are not required to be optional.Checklist