-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Reactivity: Services #20095
Comments
I'm going to be prototyping this out soon (not ™️), in library-space -- and the outcome of that will be
|
Are there any additional information about that design? Haven't thought much about it before, but I fear it could get confusing if the a class could be used as a singleton and as a having many instances at the same time. Only depending if it is consumed via class MyService {
}
class AConsumer {
@service(MyService) singleton;
@service(MyService) referenceToSameSingletonInstance;
aSecondInstance = new MyService();
aThirdInstance = new MyService();
} How would it integrate with current lookup API? Would this work?
I see how the design simplifies things a lot. But would be interested in teaching considerations and it also allows patterns, which look like a footgun. |
@jelhan that is already the case today. Literally nothing stops you from doing this: import Component from '@glimmer/component';
import MyService from '../services/my-service';
export default class Example extends Component {
myService = MyService.create();
} You shouldn’t do that, because it’s super confusing, but at the end of the day it’s just a class! (See this Twiddle for a demo of this in action.) |
A use case that I think needs to be explained here is specialization across package boundaries. Consider an addon that needs a service, but doesn't have any implementation of that service. It has only an abstract base class. The addon expects apps to provide their own implementation of the service that fills in the details. IMO this is a pretty central use case for dependency injection. I can see a few different ways to handle this case, each with some pros and cons. Customized Module ResolvingFollowing normal module resolution rules (node_modules de-facto rules as understood by typescript), the addon's component cannot import the app's implementation of the service. To make that possible, we could decide to customize module resolving, such that a special path like "$app/services/store" refers abstractly to the consuming app's module namespace. A benefit of this approach is that the module dependency graph manages to capture the real dependencies, so that the service can be loaded at an optimized time based on the set of components consuming it. A downside of this approach is that typescript will need to be specially configured so that the addon author has valid types during development (presumably they need to map Another downside of this approach is that Runtime Override RegistrationAn alternative design would be to let the addon always refer to the abstract class. It would This implies some API for connecting the two classes. Something like an explicit The benefit of this approach is that no weird resolving rules are needed and typescript would naturally understand the addon author's intent to use the abstract interface. The downside of this approach is that it probably forces the loading of the service (and the abstract base class) to become eager. It's no longer possible to "pull" the service implementation into the build as a natural side effect of pulling some component, etc, into the build. It's not clear how you could run the Async ConstructionAnother possibility would be to let service injection have an asynchronous implementation, and absorb that asynchrony while instantiating objects. A benefit of this approach is that the vast majority of the instantiations we're talking about are done within the framework, not in user code. For example, components get instantiated by glimmer, not by your own code calling Another benefit of this approach is that it would be aligned with browser capabilities and wouldn't necessarily need any special build time support. A downside of this approach is that the results may be less optimized than they could be because the build tooling cannot necessarily predict what implementation will be requested at runtime. |
I prototyping this out right now! |
Resolves #622 Experiment for: emberjs/ember.js#20095
Resolves #622 Experiment for: emberjs/ember.js#20095
Resolves #622 Experiment for: emberjs/ember.js#20095
Resolves #622 Experiment for: emberjs/ember.js#20095
Resolves #622 Experiment for: emberjs/ember.js#20095
It went ok. Lots of features we need are missing for it to be production ready, but as far as a prototype, the basics weren't bad: https://ember-resources.pages.dev/funcs/service.service |
Possibly descoped from Polaris after discussion at 2024 f2f. |
Related: emberjs/tracking-polaris#18 (comment) (since I wasn't at most of the f2f haha 🙃 -- I'll try to get myself up to date tho) |
Some basic notes:
@service
decorator can be applied to any framework object, and instantiates a resource constructor in the context of the owner associated with the framework objectThe text was updated successfully, but these errors were encountered: