/components-container

Components Container

Primary LanguageTypeScriptMIT LicenseMIT

components-container

NPM Version NPM Downloads Gandalf Status

Easy and clean way to manage async and sync components in simple and complex projects.

Getting Started

Installing

In your project run

$ npm install @soluzioni-futura/components-container

The design pattern

The components-container design pattern is inspired from PHP Slim's Dependecy Container. You have a higher order container that handles multiple components and manage to grant communication between them. You can consider a component every configured SDK, client or abstraction that allows your project to use a service or to accomplish certain operations.

Usage

const Container = require("components-container")
const mysqlComponent = require("./components/mysqlComponent.js")
const productsComponent = require("./components/productsComponent.js")

const container = new Container({
  debugTag: "container", // override of the default debug tag; default "container"
  noColors: true // enable the console colors of the debug logs; default true
})

container
  .register(mysqlComponent)
  .register(productsComponent)
  .init()

Component definition

module.exports = {
  /*
  the name property is required and represents the component handler
   */
  name: "component-name",

  /*
  the init async function is required and must return whatever you
  need to retrieve with the container get function
   */
  init: async ({ setStatus, getStatus, container }) => {
    
    // ...

    return client
  },

  /*
  the checkStatusInterval property is optional and is expressed in milliseconds
   */
  checkStatusInterval: 5000,

  /*
  the checkStatus function is optional and will be called every checkStatusInterval
   */
  checkStatus: async ({ component, setStatus }) => {
    // ...
    setStatus(STATUS.STOPPED, new Error("connection lost"))
  },

  /*
  the debugTag property overrides the default debugTag property; defaults to component name
   */
  debugTag: "debug-log-tag-name",
}

Events

components-container emits various events to fully control and customize how your application must react to status changes. Every status change inside a component will emit an event <component-name>.<new-status>. You can easily subscribe to these changes:

container.on("products.running",  async () => {
  // ...
}

Components retrieval

You can use the Container instance to easily retrieve init function return value of each component.

const mysqlClient = await container.get("mysql-component")

Example

Fake mysql component

mysql-component.js

const { STATUS } = require("components-container")


/*
this is the mysql component definition:

there are 2 required fields:
- name
- init

and 4 optional:
- checkStatusInterval
- checkStatus
- debugTag
 */

module.exports = {
  /*
  the name property is required and represents the component handler
   */
  name: "mysql",

  /*
  the init async function is required and must return whatever you
  need to retrieve with the container get function
   */
  init: async ({ setStatus, getStatus, options }) => {

    // this is a fake mysql client
    const mysqlFakeClient = {
      options,

      // the query function fakes a mysql query and returns a fake product object
      query: fakeSql => {
        if (getStatus().status !== STATUS.RUNNING) {
          throw new Error("Can't execute query, not connected to database")
        } else {
          console.log("Faking query execution:", fakeSql)
          return {
            product: {
              id: 123,
              name: "pizza",
              price: 10
            }
          }
        }
      },

      // the connect function fakes the connection to the database
      connect: () => {
        console.log("Connecting to database...")
        setTimeout(() => {
          if (getStatus().status !== STATUS.RUNNING) {
            console.log("Connection established")
            setStatus(STATUS.RUNNING)
          }
        }, 500)
      }
    }

    mysqlFakeClient.connect()

    return mysqlFakeClient
  },

  /*
  the checkStatus function is optional and will be called every checkStatusInterval
   */
  checkStatus: async ({ component, setStatus }) => {
    /*
    random switch of the component status from RUNNING to STOPPED
    and vice versa to fake ane intermittent connection
     */
    if (Math.random() > 0.5) {
      component.connect()
    } else {
      setStatus(STATUS.STOPPED, new Error("connection lost"))
    }
  },

  
  /*
  the checkStatusInterval property is optional and is expressed in milliseconds
   */
  checkStatusInterval: 5000,
    
  /*
    the debugTag property overrides the default debugTag property; defaults to component name
   */
  debugTag: "debug-log-tag-name",
}

index.js

const Container = require("components-container")

const container = new Container({
  debugTag: "container", // override of the default debug tag; default "container"
  noColors: true // enable the console colors of the debug logs; default true
})

container
  .register(require("./components/mysql.js"), { host: "example.com" })
  .register(require("./components/products.js"))
  .init()

Development

Running the tests

You can run tests running:

$ npm run test

Authors

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE file for details