mirror of
https://github.com/nasa/openmct.git
synced 2025-05-11 13:03:03 +00:00
[Framework] Document implementation
Document implementation more fully, including notes on composite services. WTD-518.
This commit is contained in:
parent
9e61e89da4
commit
ea4619c3d8
@ -2,6 +2,28 @@ Framework-level components for Open MCT Web. This is Angular and Require,
|
|||||||
with an extra layer to mediate between them and act as an extension
|
with an extra layer to mediate between them and act as an extension
|
||||||
mechanism to allow plug-ins to be introduced declaratively.
|
mechanism to allow plug-ins to be introduced declaratively.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
This section needs to be written. For now, refer to implementation notes and
|
||||||
|
examples in `example/builtins`, `example/extensions`, and `example/composite`.
|
||||||
|
|
||||||
|
## Circular dependencies
|
||||||
|
|
||||||
|
The framework layer (like Angular itself) does not support circular
|
||||||
|
dependencies among extensions. Generally, circular dependencies can be
|
||||||
|
avoided by refactoring; for instance, a dependency-less intermediary can be
|
||||||
|
added by two parties which depend upon one another, and both can depend upon
|
||||||
|
this intermediary while one abandons its dependency to the other (the
|
||||||
|
intermediary must then provide the functionality that was needed in the
|
||||||
|
abandoned dependency.)
|
||||||
|
|
||||||
|
In some cases this refactoring is non-obvious or ineffective (for instance,
|
||||||
|
when a service component depends upon the whole.) In these cases, Angular's
|
||||||
|
`$injector` may be used to break the declaration-time dependency, by allowing
|
||||||
|
retrieval of the dependency at use-time instead. (This is essentially the
|
||||||
|
same solution as above, where `$injector` acts as an application-global
|
||||||
|
generalized intermediary.)
|
||||||
|
|
||||||
# Implementation Notes
|
# Implementation Notes
|
||||||
|
|
||||||
The framework layer is responsible for performing a four-stage initialization
|
The framework layer is responsible for performing a four-stage initialization
|
||||||
@ -21,3 +43,104 @@ process. These stages are:
|
|||||||
4. __Bootstrapping.__ JSON declarations are loaded for all bundles which
|
4. __Bootstrapping.__ JSON declarations are loaded for all bundles which
|
||||||
will constitute the application, and wrapped in a useful API for subsequent
|
will constitute the application, and wrapped in a useful API for subsequent
|
||||||
stages. _Sources in `src/bootstrap`_
|
stages. _Sources in `src/bootstrap`_
|
||||||
|
|
||||||
|
Additionally, the framework layer takes responsibility for initializing
|
||||||
|
other application state. Currently this simply means adding Promise to the
|
||||||
|
global namespace if it is not defined.
|
||||||
|
|
||||||
|
## Load stage
|
||||||
|
|
||||||
|
Using Angular's `$http`, the list of installed bundles is loaded from
|
||||||
|
`bundles.json`; then, each bundle's declaration (its path + `bundle.json`)
|
||||||
|
is loaded. These are wrapped by `Bundle` objects, and the extensions they
|
||||||
|
expose are wrapped by `Extension` objects; this is only to provide a
|
||||||
|
useful API for subsequent stages.
|
||||||
|
|
||||||
|
A bundle is a set of related extensions; an extension is an individual
|
||||||
|
unit of the application that is meant to be used by other pieces of the
|
||||||
|
application.
|
||||||
|
|
||||||
|
## Resolution stage
|
||||||
|
|
||||||
|
Some, but not all, individual extensions have corresponding scripts.
|
||||||
|
These are referred to by the `implementation` field in their extension
|
||||||
|
definition. The implementation name should not include the bundle path,
|
||||||
|
or the name of the source folder; these will be pre-pended by the framework
|
||||||
|
during this stage. The implementation name should include a `.js` extension.
|
||||||
|
|
||||||
|
An extension is resolved by loading its implementing script, if one has been
|
||||||
|
declared. If none is declared, the extension's raw definition is used
|
||||||
|
instead. To ensure that extensions look similar regardless of whether or
|
||||||
|
not an implementation is present, all key-value pairs from the definition
|
||||||
|
are copied to the loaded implementation (if one has been loaded.)
|
||||||
|
|
||||||
|
## Registration stage
|
||||||
|
|
||||||
|
Following implementation resolution, extensions are registered by Angular.
|
||||||
|
How this registration occurs depends on whether or not there is built in
|
||||||
|
support for the category of extension being registered.
|
||||||
|
|
||||||
|
* For _built-in_ extension types (recognized by Angular), these are
|
||||||
|
registered with the application module. These categories are `directives`,
|
||||||
|
`controllers`, `services`, and `routes`.
|
||||||
|
* For _composite services_, extensions of category `components` are passed
|
||||||
|
to the service compositor, which builds up a dependency graph among
|
||||||
|
the components such that their fully-wired whole is exposed as a single
|
||||||
|
service.
|
||||||
|
* For _general extensions_, the resolved extensions are assembled into a
|
||||||
|
list, with Angular-level dependencies are declared, and the full set
|
||||||
|
is exposed as a single Angular "service."
|
||||||
|
|
||||||
|
### Composite services
|
||||||
|
|
||||||
|
Composite services are assumed to follow a provider-aggregator-decorator
|
||||||
|
pattern where:
|
||||||
|
|
||||||
|
* _Providers_ have dependencies as usual, and expose the API associated
|
||||||
|
with the service they compose. Providers are full service implementations
|
||||||
|
in-and-of-themselves.
|
||||||
|
* _Aggregators_ have dependencies as usual plus one additional dependency,
|
||||||
|
which will be satisfied by the array of all providers registered of
|
||||||
|
that type of service. Implementations are assumed to include an extra
|
||||||
|
argument (after what they declare in `depends`) to receive this array.
|
||||||
|
Aggregators make multiple providers appear as one.
|
||||||
|
* _Decorators_ have dependencies as usual plus one additional dependency,
|
||||||
|
which will be satisfied by either an aggregator (if one is present),
|
||||||
|
the latest provider (if no aggregator is present), or another decorator
|
||||||
|
(if multiple decorators are present.) As with aggregators, an additional
|
||||||
|
argument should be accepted by the implementation to receive this.
|
||||||
|
Decorators modify or augment the behavior of a service, but do not
|
||||||
|
provide its core functionality.
|
||||||
|
* All of the above must be declared with a `provides` property, which
|
||||||
|
indicates which type of service they compose. Providers will only be
|
||||||
|
paired with aggregators of matching types, and so on. The value of
|
||||||
|
this property is also the name of the service that is ultimately
|
||||||
|
registered with Angular to represent the composite service as a whole.
|
||||||
|
|
||||||
|
The service compositor handles this in five steps:
|
||||||
|
|
||||||
|
1. All providers are registered.
|
||||||
|
2. Arrays of providers are registered.
|
||||||
|
3. All aggregators are registered (with dependencies to the arrays
|
||||||
|
registered in the previous step.)
|
||||||
|
4. All decorators are registered (with dependencies on the most recent
|
||||||
|
components of matching types.)
|
||||||
|
5. Full composite services are registered (essentially aliasing back
|
||||||
|
to the latest component registered of a given type.)
|
||||||
|
|
||||||
|
Throughout these steps, components are registered with Angular using
|
||||||
|
generated names like `typeService[decorator#11]`. It is technically possible
|
||||||
|
to reference these dependencies elsewhere but that is not the intent.
|
||||||
|
Rather, the resulting composed service should be referred to as
|
||||||
|
`typeService` (or, more generally, the value matched from the `provides`
|
||||||
|
field of the paired service components.)
|
||||||
|
|
||||||
|
### General extensions
|
||||||
|
|
||||||
|
Similar to composite services, each individual general extension gets
|
||||||
|
registered using a generated name, like `types[extension#0]`. These are
|
||||||
|
not intended to be referenced directly; instead, they are declared
|
||||||
|
dependencies of the full list of general extensions of a given category.
|
||||||
|
This list of extensions is registered with a square-brackets suffix,
|
||||||
|
like `types[]`; this _is_ intended to be declared as a dependency by
|
||||||
|
non-framework code.
|
@ -29,8 +29,8 @@ define(
|
|||||||
// so that these can be registered separately with Angular
|
// so that these can be registered separately with Angular
|
||||||
function identify(category, extension, index) {
|
function identify(category, extension, index) {
|
||||||
var name = extension.key ?
|
var name = extension.key ?
|
||||||
(extension.key + "-" + index) :
|
("extension-" + extension.key + "#" + index) :
|
||||||
index;
|
("extension#" + index);
|
||||||
return category + "[" + name + "]";
|
return category + "[" + name + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user