docs/site/Extension-point-and-extensions.md
Extension Point/extension is a very powerful design pattern that promotes loose coupling and offers great extensibility. There are many use cases in LoopBack 4 that fit into design pattern. For example:
@loopback/boot uses BootStrapper that delegates to Booters to handle
different types of artifacts@loopback/rest uses RequestBodyParser that finds the corresponding
BodyParsers to parse request body encoded in different media types@loopback/core uses LifeCycleObserver to observe start and stop events
of the application life cycles.To add a feature to the framework and allow it to be extended, we divide the responsibility into two roles:
Extension point: it represents a common functionality that the framework depends on and interacts with, such as, booting the application, parsing http request bodies, and handling life cycle events. Meanwhile, the extension point also defines contracts for its extensions to follow so that it can discover corresponding extensions and delegate control to them without having to hard code such dependencies.
Extensions: they are implementations of specific logic for an extension point, such as, a booter for controllers, a body parser for xml, and a life cycle observer to load some data when the application is started. Extensions must conform to the contracts defined by the extension point.
NOTE: Applications can also benefit from the extension point/extensions pattern by separating common functionality and specific behaviors for the business logic.
To simplify implementations of extension point and extensions pattern on top of LoopBack 4's Inversion of Control and Dependency Injection container, the following helper decorators and functions are provided to ensure consistency and convention.
@extensionPoint: decorates a class to be an extension point with an optional
custom name@extensions: injects a getter function to access extensions to the target
extension point@extensions.view: injects a context view to access extensions to the target
extension point. The view can be listened for context events.@extensions.list: injects an array of extensions to the target extension
point. The list is fixed when the injection is done and it does not add or
remove extensions afterward.extensionFilter: creates a binding filter function to find extensions for
the named extension pointextensionFor: creates a binding template function to set the binding to be
an extension for the named extension point(s). It can accept one or more
extension point names to contribute to given extension pointsaddExtension: registers an extension class to the context for the named
extension pointInject a getter function for extensions
import {Getter} from '@loopback/core';
import {extensionPoint, extensions} from '@loopback/core';
@extensionPoint('greeters')
class GreetingService {
@extensions()
public getGreeters: Getter<Greeter[]>;
}
Inject a context view for extensions
import {ContextView} from '@loopback/core';
import {extensionPoint, extensions} from '@loopback/core';
@extensionPoint('greeters')
class GreetingService {
constructor(
@extensions.view()
public readonly greetersView: ContextView<Greeter>,
) {
// ...
}
}
Inject an array of resolved extensions
import {extensionPoint, extensions} from '@loopback/core';
@extensionPoint('greeters')
class GreetingService {
@extensions.list()
public greeters: Greeter[];
}
More usage of these helper decorators and functions are illustrated in the
greeter-extension tutorial.
The greeter-extension example provides a walk-through on how to implement the extension point/extension pattern using LoopBack 4's Context and Dependency injection container.