-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(service-proxy): add service mixin #1649
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
Conversation
e96a5d1 to
fd1e05a
Compare
|
|
||
| **NOTE**: Once we start to support declarative datasources with | ||
| `@loopback/boot`, the datasource configuration files can be dropped into | ||
| `src/datasources` to be discovered and bound automatically. |
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.
This doc fix is not strictly related to the changes made by my pull request, the section should have been removed when the datasource booter was added. Since it's just a small change, I didn't feel like creating a new commit or a new pull request.
| /** | ||
| * Interface for classes with `new` operator. | ||
| */ | ||
| export interface Class<T> { |
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.
Should we somehow leverage the Class/Constructor type from https://github.com/strongloop/loopback-next/blob/master/packages/context/src/value-promise.ts#L13 or refactor it into a common package?
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.
Having a shared definition of Class and Constructor types would be definitely nice.
However, I am not sure if such small typedefs are enough to justify the cost of creating a new package?
Also please note that in RepositoryMixin (which I based my work on), Class is coming from a different package - see https://github.com/strongloop/loopback-next/blob/f68a45ced9a392286d484b72cc690a6599aeb0c0/packages/repository/src/common-types.ts#L11-L19
Can we leave this improvement out of scope of this pull request please?
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.
Sure. +1 to have a separate PR.
| * | ||
| * @param component The component to mount services of | ||
| */ | ||
| mountComponentRepository(component: Class<{}>) { |
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.
Shouldn't it be named as mountComponentServiceProviders?
BTW, the mountComponentRepository in RepositoryMixin should be named as mountComponentRepositories too. (plural)
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.
Good point, I'll apply both suggestions.
| // tslint:disable-next-line:no-any | ||
| serviceProvider<S>(provider: Class<Provider<S>>): void; | ||
| component(component: Class<{}>): void; | ||
| mountComponentRepository(component: Class<{}>): void; |
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.
It should be mountComponentServiceProviders too.
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.
Actually, I prefer to call this method mountComponentServices. In the future, if we add support for app.service(serviceCtor: Class), then I'd like mountComponentServices to handle both service classes and providers.
| /** | ||
| * Interface for an Application mixed in with ServiceMixin | ||
| */ | ||
| export interface AppWithServices extends Application { |
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.
ApplicationWithServices?
| * | ||
| * @param component The component to mount services of | ||
| */ | ||
| mountComponentRepository(component: Class<{}>) {} |
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.
mountComponentServiceProviders
|
|
||
| // tslint:disable:no-any | ||
|
|
||
| describe('RepositoryMixin', () => { |
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.
It should be ServiceProviderMixin.
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.
See #1649 (comment), I prefer to use ServiceMixin instead of ServiceProviderMixin.
In my mind, the mixin is adding APIs for working with services. The fact that services are registered via Providers is just an implementation detail to me.
raymondfeng
left a comment
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.
Nice abstraction. Please address my comments.
|
@raymondfeng comments addressed in 767c54c, LGTY now? |
|
Nice work @bajtos . From the app developer perspective in the current way of registering the services and the proposed way of registering the provider and then injecting the service in the controller I can see a better code design and the addition of the ServiceMixin automatically by the boolean switch is great!. Can you highlight if there is a gain in performance in this way?. And finally I believe the next step would be to have the provider discovery , to avoid the registration of the provider, imagine an app with a lot of providers ? |
marioestradarosa
left a comment
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.
Excelent! .
virkt25
left a comment
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.
Awesome effort 👏 Just some minor comments.
| if (project.enableRepository) { | ||
| classWithOptionalMixins = `RepositoryMixin(${classWithOptionalMixins})`; | ||
| } | ||
| if (project.enableServices) { |
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.
Can we look to compute classWithOptionalMixins within the generator instead of the template to keep the template simple?
| "@loopback/repository": "<%= project.dependencies['@loopback/repository'] -%>", | ||
| "@loopback/rest": "<%= project.dependencies['@loopback/rest'] -%>" | ||
| "@loopback/rest": "<%= project.dependencies['@loopback/rest'] -%>", | ||
| "@loopback/service-proxy": "<%= project.dependencies['@loopback/service-proxy'] -%>" |
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.
Should this be limited to an application with serviceProxy option enabled? Then again we are loading the repository package for an application regardless ... I'll leave it up to you to decide.
| * app.serviceProvider(GeocoderServiceProvider); | ||
| * ``` | ||
| */ | ||
| serviceProvider<S>(provider: Class<Provider<S>>): void { |
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.
Shouldn't we just call this service()?
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.
Cross-posting from the pull request description:
The method name "serviceProvider" was chosen deliberately to make it
clear that we are binding a Provider, not a class constructor. Compare
this toapp.repository(MyRepo)that accepts a class construct. In the
future, we may addapp.service(MyService)method if there is enough
user demand.
|
@marioestradarosa thank you for chiming in.
I am not aware of any performance gains. My goal here is to make it easier for LB4 app developers to register their services (service providers).
Exactly! My next step is to implement a Booter in See #1439 for a bigger picture. |
767c54c to
6b7fa83
Compare
Implement "ServiceMixin" for applications. This mixin enhances component
registration so that service providers exported by a component are
automatically registered for dependency injection; and adds a new sugar
API for registering service providers manually:
app.serviceProvider(MyServiceProvicer);
The method name "serviceProvider" was chosen deliberately to make it
clear that we are binding a Provider, not a class constructor. Compare
this to `app.repository(MyRepo)` that accepts a class construct. In the
future, we may add `app.service(MyService)` method if there is enough
user demand.
6b7fa83 to
42ca652
Compare
Implement "ServiceMixin" for applications. This mixin enhances component
registration so that service providers exported by a component are
automatically registered for dependency injection; and adds a new sugar
API for registering service providers manually:
The method name "serviceProvider" was chosen deliberately to make it
clear that we are binding a Provider, not a class constructor. Compare
this to
app.repository(MyRepo)that accepts a class construct. In thefuture, we may add
app.service(MyService)method if there is enoughuser demand.
This pull request is the first step towards automated service registration via boot, see #1439.
Checklist
npm testpasses on your machinepackages/cliwere updatedexamples/*were updated