diff --git a/docs/src/design/proposals/ImperativePlugins.md b/docs/src/design/proposals/ImperativePlugins.md index 35dab663e5..c93fb48390 100644 --- a/docs/src/design/proposals/ImperativePlugins.md +++ b/docs/src/design/proposals/ImperativePlugins.md @@ -10,87 +10,155 @@ Developers will want to use bundles/plugins to (in rough order of occurrence): 1. Add new extension instances. -2. Add new service implementations. -3. Decorate service implementations. -4. Add new types of services. -5. Add new extension categories. +2. Use existing services +3. Add new service implementations. +4. Decorate service implementations. +5. Decorate extension instances. +6. Add new types of services. +7. Add new extension categories. Notably, bullets 4 and 5 above are currently handled implicitly, which has been cited as a source of confusion. ## Interfaces -```javascript +Two base classes may be used to satisfy these use cases: -/** - * Something that can be installed in a running instance of MCT. - * @interface Installable - */ + * The `CompositeServiceFactory` provides composite service instances. + Decorators may be added; the approach used for compositing may be + modified; and individual services may be registered to support compositing. + * The `ExtensionRegistry` allows for the simpler case where what is desired + is an array of all instances of some kind of thing within the system. -/** - * Install this plugin in an instance of MCT. - * @method Installable#install - * @param mct the instance of MCT in which to install - */ +Note that additional developer use cases may be supported by using the +more general-purpose `Registry` -/** - * A bundle is a set of related features that can - * be installed in MCT. - * @class - * @implements {Installable} - * @param {Metadata} metadata metadata about this bundle - */ -function Bundle(metadata) { - this.metadata = metadata; -} +```nomnoml +[Function.] -Bundle.prototype.service -Bundle.prototype.install = function (mct) {}; +[Factory. + | + - factoryFn : Function. + | + + decorate(decoratorFn : Function., options? : RegistrationOptions) +]-:>[Function.] -/** - * Data about a given entity within the system. - * @typedef Metadata - * @property {string} name the human-readable name of the entity - * @property {string} key the machine-readable identifier for the entity - * @property {string} description a human-readable summary of the entity - */ - - -[Dependency | - get() : T +[RegistrationOptions | + + priority : number or string ] -[Registry | - register( - dependencies : Dependency[], - factory : Function - ) -] +[Registry. + | + - compositorFn : Function.> + | + + register(item : T, options? : RegistrationOptions) + + composite(compositorFn : Function.>, options? : RegistrationOptions) +]-:>[Factory.] +[Factory.]-:>[Factory.] -[ExtensionRegistry | -] - -[ExtensionRegistry] +[ExtensionRegistry.]-:>[Registry.>] +[Registry.>]-:>[Registry.] +[CompositeServiceFactory.]-:>[Registry.] +[Registry.]-:>[Registry.] ``` -Creating a bundle then looks like: +## Examples -```javascript -define([ - 'mct', - './SomeExtension', - './SomeService' -], function (mct, SomeExtension, SomeService) { - var plugin = new mct.Bundle({ - key: 'myBundle', - name: "My bundle", - description: "A bundle that I made." - }); +### 1. Add new extension instances. - plugin.extension - - return plugin; +```js +// Instance-style registration +mct.types.register(new mct.Type({ + key: "timeline", + name: "Timeline", + description: "A container for activities ordered in time." }); +// Factory-style registration +mct.actions.register(function (domainObject) { + return new RemoveAction(domainObject); +}, { priority: 200 }); ``` + +### 2. Use existing services + +```js +mct.actions.register(function (domainObject) { + var dialogService = mct.ui.dialogServiceFactory(); + return new PropertiesAction(dialogService, domainObject); +}); +``` + +### 3. Add new service implementations + +```js +// Instance-style registration +mct.persistenceServiceFactory.register(new LocalPersistenceService()); + +// Factory-style registration +mct.persistenceServiceFactory.register(function () { + var $http = angular.injector(['ng']).get('$http'); + return new LocalPersistenceService($http); +}); +``` + +### 4. Decorate service implementations + +```js +mct.modelServiceFactory.decorate(function (modelService) { + return new CachingModelDecorator(modelService); +}, { priority: 100 }); +``` + +### 5. Decorate extension instances + +```js +mct.capabilities.decorate(function (capabilities) { + return capabilities.map(decorateIfApplicable); +}); +``` + +This use case is not well-supported by these API changes. The most +common case for decoration is capabilities, which are under reconsideration; +should consider handling decoration of capabilities in a different way. + +### 6. Add new types of services + +```js +myModule.myServiceFactory = new mct.CompositeServiceFactory(); + +// In cases where a custom composition strategy is desired +myModule.myServiceFactory.composite(function (services) { + return new MyServiceCompositor(services); +}); +``` + +### 7. Add new extension categories. + +```js +myModule.hamburgers = new mct.ExtensionRegistry(); +``` + +## Evaluation + +### Benefits + +* Encourages separation of registration from declaration (individual + components are decoupled from the manner in which they are added + to the architecture.) +* Minimizes "magic." Dependencies are acquired, managed, and exposed + using plain-old-JavaScript without any dependency injector present + to obfuscate what is happening. +* Offers comparable expressive power to existing APIs; can still + extend the behavior of platform components in a variety of ways. +* Does not force or limit formalisms to use; + +### Detriments + +* Does not encourage separation of dependency acquisition from + declaration; that is, it would be quite natural using this API + to acquire references to services during the constructor call + to an extension or service. But, passing these in as constructor + arguments is preferred (to separate implementation from architecture.) +* Adds (negligible?) boilerplate relative to declarative syntax.