Skip to content
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

Spike: Application service layer with @service decorator (like Java Spring) #2239

Closed
haotangio opened this issue Jan 11, 2019 · 9 comments
Closed

Comments

@haotangio
Copy link
Contributor

haotangio commented Jan 11, 2019

As far as I understand, currently if we develop application with LB 4, most of the application logic will be handled by controllers (belongs to presentation layer) which are injected repositories (belongs to data access layer). This may make controller very "fat" (fat controller), and this is where we mix the application logic together with transport stuff (routing, HTTP response status, ...), which is somehow not so cool for complex use cases.

Moreover, some controllers may share the same application logic, or you even don't need a controller to fulfill a use case (e.g: seeding initial data on system startup). This is where a service object which encapsulates application logic would be helpful. It then could be used in many scenarios depending on external triggers (not only REST/SOAP/GraphQL controllers, but also cron-job handlers, application message-queue event-listeners, ...) via DI.

IMHO, Loopback 4 are kind of similar (or inspired?) by not only MVC, but also DDD (Domain-Driven Design). The usage of controller, repository, entity, factory, context, ... are also familiar to Java Spring. How if we have a similar @service decorator as well? Just my personal thought :)

@bajtos
Copy link
Member

bajtos commented Jan 22, 2019

I think this is similar to the problem we encountered in loopbackio/loopback4-example-shopping#26, where the implementation of login method does not belong to repository nor controller, and we needed a new place (a service? a utility?) to provide implementation shared by multiple callers (controllers, tests, etc.).

@bajtos bajtos added the feature label Jan 22, 2019
@jannyHou
Copy link
Contributor

jannyHou commented Jan 22, 2019

+1 for Miroslav's comment above, we currently implemented it as a utility. I am not very expert on the difference between service and utility, correct me if I am wrong: my point is if it's only for app/api developer's internal use, utility is good, if we give client access to it, then implement it as a service.

@luisfavila
Copy link

luisfavila commented Jan 29, 2019

What about the cron-job/queue use case? Is there any way in the current implementation to have a controller be a singleton?

@bajtos
Copy link
Member

bajtos commented Feb 1, 2019

Is there any way in the current implementation to have a controller be a singleton?

It is certainly possible to have a singleton controller class, see SINGLETON scope.

I don't think that's a good design though. Controllers should be lightweight classes with a new instance created for each request, so that they can receive request-specific values via dependency injection.

Instead of a singleton controller, you should have a singleton service that's holding cron-job/queue state, and inject this service into per-request controller instances.

FWIW, singletons will get you only so far. Once you start scaling your app horizontally and run multiple worker processes, you will need to find a different way how to share state. Most likely using a key-value store like Redis.

@bajtos bajtos changed the title Suggestion: Application service layer with @service decorator (like Java Spring) Spike: Application service layer with @service decorator (like Java Spring) Feb 1, 2019
@bajtos bajtos added the spike label Feb 1, 2019
@bajtos
Copy link
Member

bajtos commented Feb 1, 2019

Let's convert this story into a spike where we will identify how we want to design LB4 to support services and define follow-up stories to implement necessary changes.

User stories to consider:

  • As an extension developer, I want to implement a service and export it so that it's bound to target application context.
  • As an application developer, I want to implement a service and have it automatically discovered & bound to context by @loopback/boot.
  • When implementing a service, I want to use DI to receive repository instances and service proxies.
  • As an application developer, I want to inject a service to my controllers.
  • As an application developer, I want to write a unit test for my controller depending on a service and use a mocked version of the service.

We also need to figure out the relation to service-proxy concept as described in Calling other APIs and web services to make it clear what's similar, what is different and when to use which concept.

Other areas to keep in consideration when creating follow-up stories:

  • Documentation updates, I think we should create a new page in "Key concepts"
  • CLI tooling, e.g. lb4 service
  • Naming. Do we want to use service? I feel it may be too generic and thus confusing (not really telling us what are services about).

@raymondfeng
Copy link
Contributor

@haotangio We have a starting point here - https://loopback.io/doc/en/lb4/Binding.html#configure-binding-attributes-for-a-class.

Now a class can be decorated with @bind with additional metadata. IMO, @service can be a sugar decorator of @bind. What matters is if we want to introduce a new concept to represent such reusable and injectable classes and possibly make them discoverable as part of boot.

@bajtos bajtos added 2019Q3 and removed 2019Q2 labels Apr 16, 2019
@dhmlau dhmlau added 2019Q4 and removed 2019Q3 labels Jun 25, 2019
@raymondfeng
Copy link
Contributor

I think we now have enough capabilities to support the service idea:

  1. Each TypeScript class (not only controllers) can be decorated with @bind and @inject to be managed by IoC and DI.

  2. We have improved lb4 service to generate local services - see https://loopback.io/doc/en/lb4/Service-generator.html

  3. We support life cycle observers to allow use cases such as startup scripts or cron jobs - https://loopback.io/doc/en/lb4/Life-cycle.html

Please let us know what's missing to support your scenarios.

@haotangio
Copy link
Contributor Author

Hello @raymondfeng. Thank you very much for your update.

I'm starting to use LB4 for several projects ahead and just checked in the current version of LB4.
And yeah, I think the current IoC and DI support is great enough to build production products.

The only lacking features to be perfect-to-use (in my feeling as framework users/developers) is about has-many-through relation, and built-in authorization with ACL. But it should be in a different issue ticket :)

@dhmlau
Copy link
Member

dhmlau commented Oct 15, 2019

@haotangio, thanks for your support for LoopBack 4!

For hasMany Through, there's an ongoing PR to add the support: #2359.
For built-in authorization, please take a look at our docs: https://loopback.io/doc/en/lb4/Loopback-component-authorization.html.

I'd like to close this issue as resolved. Please feel free to continue the hasMany Through discussion in the above PR or open a new GitHub issues for other areas. Thanks!!

@dhmlau dhmlau closed this as completed Oct 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants