Table of Contents

Overview

The framework layer's most basic responsibility is allowing individual software components to communicate. The software components it recognizes are:

The framework layer operates by taking a set of active bundles, and exposing extensions to one another as-needed, using dependency injection. Extensions are responsible for declaring their dependencies in a manner which the framework layer can understand.

Diagram 1

The "dependency injection framework" in this case is AngularJS. Open MCT's framework layer is really just a thin wrapper over Angular that recognizes the concepts of bundles and extensions (as declared in JSON files) and registering extensions with Angular. It additionally acts as a mediator between Angular and RequireJS, which is used to load JavaScript sources which implement extensions.

Diagram 2

It is worth noting that no other components are "aware" of the framework component directly; Angular and Require are used by the framework components, and extensions in various bundles will have their dependencies satisfied by Angular as a consequence of registration activities which were performed by the framework component.

Application Initialization

The framework component initializes an Open MCT application following a simple sequence of steps.

Diagram 3

  1. Loading bundles.json. A file named bundles.json is loaded to determine which bundles to load. Bundles are given in this file as relative paths which point to bundle directories.
  2. Load bundle.json files. Individual bundle definitions are loaded; a bundle.json file is expected in each bundle directory.
  3. Resolving implementations. Any scripts which provide implementations for extensions exposed by bundles are loaded, using RequireJS.
  4. Register with Angular. Resolved extensions are registered with Angular, such that they can be used by the application at run-time. This stage includes both registration of Angular built-ins (directives, controllers, routes, constants, and services) as well as registration of non-Angular extensions.
  5. Bootstrap application. Once all extensions have been registered, the Angular application is bootstrapped.

Architectural Paradigm

Diagram 4

Open MCT's architecture relies on a simple premise: Individual units (extensions) only have access to the dependencies they declare that they need, and they acquire references to these dependencies via dependency injection. This has several desirable traits:

A drawback to this approach is that it makes it difficult to define "the architecture" of Open MCT, in terms of describing the specific units that interact at run-time. The run-time architecture is determined by the framework as the consequence of wiring together dependencies. As such, the specific architecture of any given application built on Open MCT can look very different.

Keeping that in mind, there are a few useful patterns supported by the framework that are useful to keep in mind.

The specific service infrastructure provided by the platform is described in the Platform Architecture.

Extension Categories

One of the capabilities that the framework component layers on top of AngularJS is support for many-to-one dependencies. That is, a specific extension may declare a dependency to all extensions of a specific category, instead of being limited to declaring specific dependencies.

Diagram 5

This is useful for introducing specific extension points to an application. Some unit of software will depend upon all extensions of a given category and integrate their behavior into the system in some fashion; plugin authors can then add new extensions of that category to augment existing behaviors.

Some developers may be familiar with the use of registries to achieve similar characteristics. This approach is similar, except that the registry is effectively implicit whenever a new extension category is used or depended-upon. This has some advantages over a more straightforward registry-based approach:

Composite Services

Composite services (registered via extension category components) are a pattern supported by the framework. These allow service instances to be built from multiple components at run-time; support for this pattern allows additional bundles to introduce or modify behavior associated with these services without modifying or replacing original service instances.

Diagram 6

In this pattern, components all implement an interface which is standardized for that service. Components additionally declare that they belong to one of three types:

The framework will register extensions in this category such that an aggregator will depend on all of its providers, and decorators will depend upon on one another in a chain. The result of this compositing step (the last decorator, if any; otherwise the aggregator, if any; otherwise a single provider) will be exposed as a single service that other extensions can acquire through dependency injection. Because all components of the same type of service expose the same interface, users of that service do not need to be aware that they are talking to an aggregator or a provider, for instance.