1.0.0 • Published 5 years ago

@ximerajs/kernel v1.0.0

Weekly downloads
1
License
-
Repository
-
Last release
5 years ago

Ximera Micro-Kernel

Ximera micro-kernel is a clean foundation for your Node.JS micro-service, optionally running inside a Kubernetes cluster.

Getting started

Just install @ximerajs/kernel in your Node.js project. Create an index.js file and copy the following code:

const kernelFactory = require('@ximerajs/kernel');
const kernel = kernelFactory({
    plugins: [
        require('@ximerajs/mongodb')
    ],
    ecosystem: {
        protocol: 'xmsp1',
        registry: {
            type: 'redis',
            host: 'localhost',
            port: 6379,
            namespace: 'ximera'
        }
    },
    logger: {
        syslog: {
            level: process.env.SYSLOG_LEVEL || 'info'
        },
        console: {
            level: process.env.LOG_LEVEL || 'debug'
        }
    }
}).bootstrap();

This will intantiate the kernel, discover all your components, locally, located in your cluster or provided by the kernel or any installed plugins. This discovery approach give you a powerful reuse capability as well as plenty of neat abstraction tricks to inject all required dependencies for your components to perform their business logic.

Ximera supports a limited number of builtin component types, the most important being type, which is the root type for all other types. But the power is the capacity, for your own micro-service or plugins, to define new types that will be handled exactly like builtin types. These types can use local files to discover components or use the ecosystem registry, kubernetes or any kind of remote discovery mecanism. The details of how a type discover and load its components is completely abstracted away by the type implementation. Ximera provides a number of type helpers to simplify type creation, but types can override and extend the framework to abstract about any kind of dynamic components.

For example, when importing the mongodb plugin (as in the example above), you get a new collection type. This type will enable you to inject any mongodb collections (with added capabilities), into your own component, without having to declare it anywhere. The whole wiring of how the collection is accessed is hidden by the type.

module.exports.factory = function({ users$collection }) {
    return user$collection.findOne({ username: "jgrenon"});
}
module.exports.waitFor = ['users$collection'];

This simple logic component uses the users collection from a default mongodb database. The collection will be injected automatically, no need to declare it anywhere.

If you want to specify the actual database, you can inject users$collection$mydb instead, which will use the specified db instead of the default one.

Ecosystem

Ximera kernel power comes into play when running a micro-service as part of a kubernetes cluster or a custom service ecosystem. Managing service interactions is not as simple as it might seem, as each service is evolved independently, accessing the right version is somethings complex. The actual ecosystem serving a request might be dictated by the client version and thus, multiple versions of an ecosystem can co-exists in the same kubernetes cluster, complicating things up a lot. It's not cost effective to create a kubernetes cluster for each app ecosystem, some services are shared between apps, only a few have new versions, well, the best approach is to find a way to create a logic service mesh, dictated by client version.

Injectting a remote service

You can communicate with a remote service by injecting it in your own component.

module.exports.factory = function({ employee$store$employee }) {
    return employee$store$employee.list({ dept: 'accounting', age: {$gt: 30}});
};
module.exports.waitFor = ['employee$store$employee'];

This simple component create a remote references to the employee store of the employee service. Services can expose multiple components. The last parameter is used as the hostname in this case, which is recognized by the store type to properly resolve the service.

AS you can see, we use the waitFor component clause to indicate to the kernel that we want to wait for the listed promises to be resolved before our factory is executed. This cleans our factory code a bit, avoid us to await for a bunch of promises. WaitFor can also help clean the names and specify target versions.

module.exports.factory = function({ employees }) {
    return employees.list({ dept: 'accounting', age: {$gt: 30}});
};
module.exports.waitFor = { 
    deps: [{ key: 'employees', ref: 'employee$store$employee', version: '1.1'], 
    timeout: 10000
};

When a client application context is available, the version can be set to $clientVersion, which will resolve to the configured service version for the specified client version, of the client version directly if no ecosystem manifest is available.

ecosystem registry (tbd)

client context (tbd)

tracing (tbd)

injection operators (list, map, filter, observe) (tbd)

  • _all$ operator
  • _observable$ : stream dependencies and send new ones when detected
  • _map$
  • _filter$

config injection and monitoring (tbd)

default configuration (when first executed) mongodb_url$config -> mapped to mongodb.url in service configuration. Overriden by env variable. stored in redis

logging (tbd)

Plugins

Plugins are npm modules that can be injected in your micro-service to extend its capacity.

  • mongodb
  • redis
  • kubernetes

Services

Services are ready to be deployed services powered by Ximera, serving components for other service to remotely access. They are usually associated with an existing Docker image and kubernetes deployment file for easily deployment in your cluster.

  • security
  • notification
  • preferences