-
Notifications
You must be signed in to change notification settings - Fork 5
sp core library.servicescope
Home > @microsoft/sp-core-library > ServiceScope
The service locator pattern used by the SharePoint Framework.
Signature:
export default class ServiceScope
ServiceScope provides a formalized way for components to register and consume dependencies ("services"), and to enable different implementations to be registered in different scopes. This improves modularity by decoupling components from their dependencies in an extensible way.
For example, suppose that various components need access to an IPageManager instance. We could simply make the PageManager a singleton (i.e. global variable), but this will not work e.g. if we need to create a pop-up dialog that requires a second PageManager instance. A better solution would be to add the PageManager as a constructor parameter for each component that requires it, however then we immediately face the problem that any code that calls these constructors also needs a PageManager parameter. In an application with many such dependencies, business logic that ties together many subsystems would eventually pick up a constructor parameter for every possible dependency, which is awkward. A natural solution would be to move all the dependencies into a class with name like "ApplicationContext", and then pass this around as our constructor parameter. This enables the PageManager to be passed to classes that need it without cluttering the intermediary classes that don't. However, it still has a design problem that "ApplicationContext" has hard-coded dependencies on many unrelated things. A more flexible approach is to make it a dictionary that can look up items for consumers/providers who know the right lookup key (i.e. ServiceKey). This is the popular "service locator" design pattern, familiar from the SPContext API in classic SharePoint.
ServiceScope takes this idea a step further in two important ways: First, it provides a scoping mechanism so that e.g. if we have two different pages, they can each provide a unique PageManager instance while still sharing other common dependencies. Secondly, it allows for a ServiceKey to provide a default implementation of the dependency. This is important for API stability in our modular client-side environment: For example, suppose that version 2.0 of our application introduced a new IDiagnosticTracing interface that a version 2.0 component will expect to consume. If the version 2.0 component gets loaded by an older 1.0 application, it would fail. We could fix this by requiring each consumer to check for any missing dependencies and handle that case, but it would require a lot of checks. A better solution is to ensure that a default implementation always exists, perhaps just a trivial behavior, so that components can assume that consume() will always return some object that implements the contract.
Usage: ServiceScope instances are created by calling either ServiceScope.startNewRoot() or ServiceScope.startNewChild(). They are initially in an "unfinished" state, during which provide() can be called to register service keys, but consume() is disallowed. After ServiceScope.finish() is called, consume() is allowed and provide() is now disallowed. These semantics ensure that ServiceScope.consume() always returns the same result for the same key, and does not depend on order of initialization. It also allows us to support circular dependencies without worrying about infinite loops. (Circular dependencies are best avoided, however this is difficult to guarantee when working with components that were contributed by various third parties without any coordination.) To avoid mistakes, it's best to always call consume() inside a callback from serviceScope.whenFinished().
Constructor | Modifiers | Description |
---|---|---|
(constructor)(parent) | Constructs a new instance of the ServiceScope class |
Method | Modifiers | Description |
---|---|---|
consume(serviceKey) | Consumes a service from the service scope. | |
createAndProvide(serviceKey, simpleServiceClass) | This is a shorthand function that is equivalent to constructing a new instance of the simpleServiceClass, then registering it by calling ServiceScope.provide(). | |
createDefaultAndProvide(serviceKey) | This is a shorthand function that constructs the default implementation of the specified serviceKey, and then registers it by calling ServiceScope.provide(). | |
finish() | Completes the initialization sequence for a service scope. | |
getParent() | Returns the parent of the current ServiceScope, or undefined if this is a root scope. | |
provide(serviceKey, service) | Add a new service to a service scope. | |
startNewChild() | Constructs a new ServiceScope that is a child of the current scope. | |
startNewRoot() | static |
Create a new root-level ServiceScope. Only root-level scopes have the ability to autocreate default implementations of ServiceKeys. |
whenFinished(callback) | Defer an operation until after ServiceScope.finish() has completed. |