Skip to content

Instantly share code, notes, and snippets.

@bajtos
Created September 14, 2014 07:39
Show Gist options
  • Save bajtos/23032722590952451e94 to your computer and use it in GitHub Desktop.
Save bajtos/23032722590952451e94 to your computer and use it in GitHub Desktop.
LoopBack Components - a strawman proposal

LoopBack Components - a strawman proposal

Component definition

Metadata

Component metadata is stored in package.json under loopback-component property.

// package.json
{
  "loopback-component": {
    "facets": {
      "server": {
        "dependencies": [
          // a dependency specific to this facet only
          "./common"
        ],
        "settings": {
          // LDL description of config options
        }
      },

      "common": {
        "dependencies": [
        ],
        "settings": {
          // LDL description of config options
        }
      },
    }
    "dependencies": [
      // a shortcut version for declaring a dependency used by all facets
      // server facet depends on loopback-component-push/server,
      // common facet depends on loopback-component-push/common,
      // etc.
      "loopback-component-push"
    ]
  } 
}

This has a nice side-effect that the workspace does not need to scan the filesystem to find what facets the project contains - all metadata is stored in package.json.

Main script file

// server/index.js
module.exports = function(app, options, next) {
  // mount an express middleware
  app.use(compress());

  // add a route
  app.get('/login', login);

  // produce an event
  app.emit('strawman', this);

  next();
}

Exporting a datasource

// server/datasources.json
{
  "push": {
    "connector": "../connector", // facet-relative path
    // etc."
   }
}

Note: all datasources are exported, it is not possible to create a facet-private datasource.

loopback-boot should load datasources provided by all dependencies listed in app's facet.json (recursively).

Exporting a model

// server/models/foo.json
{
  "name": "Foo",
  "properties": {
    // etc.
  },
  // etc.
}

Note: all models are exported, loopback-boot will load only the models that are configured by the app.

For each dependency listed in app's facet.json (recursively), loopback-boot should add {facet}/models to the directories scanned for model definitions.

Connectors

Since connectors are resolved through require, components should provide a standalone file for each connector. E.g. loopback-component-push/connector.

To allow components to use their connectors in datasources.json, loopback should resolve relative paths in connector names before calling require.

Component consumption

To use component, one has to:

  • npm install the package
  • add component facets as dependencies of app facets
  • provide facet configuration

Facet dependencies

This uses the same mechanism as described in "Component definition" above.

Facet configuration

Many components require a user-provided configuration, e.g. the push component requires auth credentials for APNS and GCM.

The configuration must be dynamic, i.e. the developer should be able to specify different values for development and production and also compute the values using arbitrary javascript code.

Since facets can depend on another facets, there should be a way how to forward/transform the configuration provided by the app to the facets that are dependencies of app facets.

// in the app project
// server/facet-config.json
// (merged with server/facet-config.production.*, etc.)
{
  "loopback-component-push/server": {
    "gcm": { "key": " ..." },
    "job-queue-name": "foobar-push-notifications",
    // etc.
  }
}

// in the component
// node_modules/loopback-component-push/server/configure.js
// @param facetConfig the initial configuration provided by facet-config.json
// @param settings settings object provided by the app
// @param next callback
module.exports = function(facetConfig, settings, next) {
  // configure loopback-component-job-dispatcher that we are depending on
  facetConfig['loopback-component-job-dispatcher'] = {
    // use the value provided in our settings
    'queue-name': 'loopback push' || settings['job-queue-name']
  };
}

We can support configure.js in top-level applications too, in which case the settings object can be the configuration provided by config.json.

Drawbacks: facet configuration is coupled with the configuration of depending component facets (i.e. the config code has to know which components/facets are listed as dependencies), but the configuration lives in a different file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment