Step 7. Wrapping the microservice into a container

Our service is pretty much done - all that is left is to place the components we’ve developed into a process container and configure it.

When a container is started, it starts composing the microservice out of the components indicated in the configuration file. For the container to be able to build these components, it will need a component factory. In the build directory, create a BeaconsServiceFactory class and populate it with the following code:

/src/build/BeaconsServiceFactory.ts

import { Factory } from 'pip-services3-components-nodex';
import { Descriptor } from 'pip-services3-commons-nodex';

import { BeaconsMemoryPersistence } from '../../src/persistence/BeaconsMemoryPersistence';
import { BeaconsFilePersistence } from '../../src/persistence/BeaconsFilePersistence';
import { BeaconsMongoDbPersistence } from '../../src/persistence/BeaconsMongoDbPersistence';
import { BeaconsController } from '../../src/logic/BeaconsController';
import { BeaconsHttpServiceV1 } from '../../src/services/version1/BeaconsHttpServiceV1';

export class BeaconsServiceFactory extends Factory{
    public static MemoryPersistenceDescriptor = new Descriptor('beacons', 'persistence', 'memory', '*', '1.0');
    public static FilePersistenceDescriptor = new Descriptor('beacons', 'persistence', 'file', '*', '1.0');
    public static MongoDbPersistenceDescriptor = new Descriptor('beacons', 'persistence', 'mongodb', '*', '1.0');
    public static ControllerDescriptor = new Descriptor('beacons', 'controller', 'default', '*', '1.0');
    public static HttpServiceV1Descriptor = new Descriptor('beacons', 'service', 'http', '*', '1.0');
    
    constructor(){
        super();

        this.registerAsType(BeaconsServiceFactory.MemoryPersistenceDescriptor, BeaconsMemoryPersistence);
        this.registerAsType(BeaconsServiceFactory.FilePersistenceDescriptor, BeaconsFilePersistence);
        this.registerAsType(BeaconsServiceFactory.MongoDbPersistenceDescriptor, BeaconsMongoDbPersistence);
        this.registerAsType(BeaconsServiceFactory.ControllerDescriptor, BeaconsController);
        this.registerAsType(BeaconsServiceFactory.HttpServiceV1Descriptor, BeaconsHttpServiceV1);
    }
}

/src/service/build/BeaconsServiceFactory.cs

namespace Beacons.Build
{
    public class BeaconsServiceFactory : Factory
    {
        public static Descriptor Descriptor = new Descriptor("beacons", "factory", "service", "default", "1.0");
        public static Descriptor MemoryPersistenceDescriptor = new Descriptor("beacons", "persistence", "memory", "*", "1.0");
        public static Descriptor MongoDbPersistenceDescriptor = new Descriptor("beacons", "persistence", "mongodb", "*", "1.0");
        public static Descriptor ControllerDescriptor = new Descriptor("beacons", "controller", "default", "*", "1.0");
        public static Descriptor HttpServiceDescriptor = new Descriptor("beacons", "service", "http", "*", "1.0");


        public BeaconsServiceFactory()
        {
            RegisterAsType(MemoryPersistenceDescriptor, typeof(BeaconsMemoryPersistence));
            RegisterAsType(MongoDbPersistenceDescriptor, typeof(BeaconsMongoDbPersistence));
            RegisterAsType(ControllerDescriptor, typeof(BeaconsController));
            RegisterAsType(HttpServiceDescriptor, typeof(BeaconsHttpServiceV1));
        }
    }
}

/build/BeaconsServiceFactory.go

package build

import (
	logic "github.com/pip-services-samples/service-beacons-gox/logic"
	persist "github.com/pip-services-samples/service-beacons-gox/persistence"
	services1 "github.com/pip-services-samples/service-beacons-gox/services/version1"
	cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
)

type BeaconsServiceFactory struct {
	*cbuild.Factory
}

func NewBeaconsServiceFactory() *BeaconsServiceFactory {
	c := &BeaconsServiceFactory{
		Factory: cbuild.NewFactory(),
	}

	memoryPersistenceDescriptor := cref.NewDescriptor("beacons", "persistence", "memory", "*", "1.0")
	postgresPersistenceDescriptor := cref.NewDescriptor("beacons", "persistence", "postgres", "*", "1.0")
	mongoPersistenceDescriptor := cref.NewDescriptor("beacons", "persistence", "mongodb", "*", "1.0")
	filePersistenceDescriptor := cref.NewDescriptor("beacons", "persistence", "file", "*", "1.0")
	controllerDescriptor := cref.NewDescriptor("beacons", "controller", "default", "*", "1.0")
	httpServiceV1Descriptor := cref.NewDescriptor("beacons", "service", "http", "*", "1.0")

	c.RegisterType(postgresPersistenceDescriptor, persist.NewBeaconsPostgresPersistence)
	c.RegisterType(mongoPersistenceDescriptor, persist.NewBeaconsMongoPersistence)
	c.RegisterType(memoryPersistenceDescriptor, persist.NewBeaconsMemoryPersistence)
	c.RegisterType(filePersistenceDescriptor, persist.NewBeaconsFilePersistence)
	c.RegisterType(controllerDescriptor, logic.NewBeaconsController)
	c.RegisterType(httpServiceV1Descriptor, services1.NewBeaconsHttpServiceV1)

	return c
}

/lib/build/BeaconsServiceFactory.dart

import 'package:pip_services3_components/pip_services3_components.dart';
import 'package:pip_services3_commons/pip_services3_commons.dart';

import '../persistence/BeaconsMemoryPersistence.dart';
import '../persistence/BeaconsFilePersistence.dart';
import '../persistence/BeaconsMongoDbPersistence.dart';
import '../logic/BeaconsController.dart';
import '../services/version1/BeaconsCommandableHttpServiceV1.dart';

class BeaconsServiceFactory extends Factory {
  static final MemoryPersistenceDescriptor =
      Descriptor('beacons', 'persistence', 'memory', '*', '1.0');
  static final FilePersistenceDescriptor =
      Descriptor('beacons', 'persistence', 'file', '*', '1.0');
  static final MongoDbPersistenceDescriptor =
      Descriptor('beacons', 'persistence', 'mongodb', '*', '1.0');
  static final ControllerDescriptor =
      Descriptor('beacons', 'controller', 'default', '*', '1.0');
  static final CommandableHttpServiceV1Descriptor =
      Descriptor('beacons', 'service', 'commandable-http', '*', '1.0');
  
  BeaconsServiceFactory() : super() {
    registerAsType(BeaconsServiceFactory.MemoryPersistenceDescriptor,
        BeaconsMemoryPersistence);
    registerAsType(BeaconsServiceFactory.FilePersistenceDescriptor,
        BeaconsFilePersistence);
    registerAsType(BeaconsServiceFactory.MongoDbPersistenceDescriptor,
        BeaconsMongoDbPersistence);
    registerAsType(
        BeaconsServiceFactory.ControllerDescriptor, BeaconsController);
    registerAsType(BeaconsServiceFactory.CommandableHttpServiceV1Descriptor,
        BeaconsCommandableHttpServiceV1);
    
  }
}

/src/build/BeaconsServiceFactory.py

from pip_services3_commons.refer import Descriptor
from pip_services3_components.build import Factory

from ..logic.BeaconsController import BeaconsController
from ..persistence.BeaconsFilePersistence import BeaconsFilePersistence
from ..persistence.BeaconsMemoryPersistence import BeaconsMemoryPersistence
from ..persistence.BeaconsMongoDbPersistence import BeaconsMongoDbPersistence
from ..services.version1.BeaconsHttpServiceV1 import BeaconsHttpServiceV1


class BeaconsServiceFactory(Factory):

    MemoryPersistenceDescriptor = Descriptor('beacons', 'persistence', 'memory', '*', '1.0')
    FilePersistenceDescriptor = Descriptor('beacons', 'persistence', 'file', '*', '1.0')
    MongoDbPersistenceDescriptor = Descriptor('beacons', 'persistence', 'mongodb', '*', '1.0')
    ControllerDescriptor = Descriptor('beacons', 'controller', 'default', '*', '1.0')
    HttpServiceV1Descriptor = Descriptor('beacons', 'service', 'http', '*', '1.0')

    def __init__(self):
        super(BeaconsServiceFactory, self).__init__()

        self.register_as_type(BeaconsServiceFactory.MemoryPersistenceDescriptor, BeaconsMemoryPersistence)
        self.register_as_type(BeaconsServiceFactory.FilePersistenceDescriptor, BeaconsFilePersistence)
        self.register_as_type(BeaconsServiceFactory.MongoDbPersistenceDescriptor, BeaconsMongoDbPersistence)
        self.register_as_type(BeaconsServiceFactory.ControllerDescriptor, BeaconsController)
        self.register_as_type(BeaconsServiceFactory.HttpServiceV1Descriptor, BeaconsHttpServiceV1)
Not available

As shown in the code above, we start by creating descriptors for all of our components, and then, in the constructor, we register each component in the factory using its descriptor.

Now let’s move on to creating the container itself. In the container directory, create a BeaconsProcess file with the following code:

/src/containers/BeaconsProcess.ts

import { ProcessContainer } from 'pip-services3-container-nodex';
import { DefaultRpcFactory } from 'pip-services3-rpc-nodex';
import { DefaultSwaggerFactory } from 'pip-services3-swagger-nodex';

import {BeaconsServiceFactory} from '../build/BeaconsServiceFactory';

export class BeaconsProcess extends ProcessContainer{
    public constructor(){
        super('beacons', 'Beacons microservice');

        this.addFactory(new BeaconsServiceFactory());
        this.addFactory(new DefaultRpcFactory());
        this.addFactory(new DefaultSwaggerFactory());
    }
}

/src/service/containers/BeaconsProcess.cs

namespace Beacons.Containers
{
    public class BeaconsProcess : ProcessContainer
    {
        public BeaconsProcess()
            : base("beacons", "Beacons microservice")
        {
            AddFactory(new DefaultRpcFactory());
            AddFactory(new DefaultSwaggerFactory());
            AddFactory(new BeaconsServiceFactory());
        }
    }
}

/containers/BeaconsProcess.go

package containers

import (
	factory "github.com/pip-services-samples/service-beacons-gox/build"
	cproc "github.com/pip-services3-gox/pip-services3-container-gox/container"
	cpg "github.com/pip-services3-gox/pip-services3-postgres-gox/build"
	rbuild "github.com/pip-services3-gox/pip-services3-rpc-gox/build"
)

type BeaconsProcess struct {
	*cproc.ProcessContainer
}

func NewBeaconsProcess() *BeaconsProcess {
	c := &BeaconsProcess{
		ProcessContainer: cproc.NewProcessContainer("beacons", "Beacons microservice"),
	}

	c.AddFactory(factory.NewBeaconsServiceFactory())
	c.AddFactory(rbuild.NewDefaultRpcFactory())
	c.AddFactory(cpg.NewDefaultPostgresFactory())

	return c
}


import 'package:pip_services3_container/pip_services3_container.dart';
import 'package:pip_services3_rpc/pip_services3_rpc.dart';

import '../build/BeaconsServiceFactory.dart';

class BeaconsProcess extends ProcessContainer {
  BeaconsProcess() : super('beacons', 'Beacons microservice') {
    factories.add(BeaconsServiceFactory());
    factories.add(DefaultRpcFactory());
  }
}

/src/containers/BeaconsProcess.ts

import sys

from pip_services3_container.ProcessContainer import ProcessContainer
from pip_services3_rpc.build.DefaultRpcFactory import DefaultRpcFactory
from pip_services3_swagger.build.DefaultSwaggerFactory import DefaultSwaggerFactory

from ..build.BeaconsServiceFactory import BeaconsServiceFactory


class BeaconsProcess(ProcessContainer):
    def __init__(self):
        super(BeaconsProcess, self).__init__('beacons', 'Beacons microservice')

        self._factories.add(BeaconsServiceFactory())
        self._factories.add(DefaultRpcFactory())
        self._factories.add(DefaultSwaggerFactory())

Not available

Next, add the factories that are missing from the standard container (the one from the pip-services3-container module), so that we can build all the objects our service needs. In our case, this means adding the factory for the components we’ve written, as well as the default RPC factory (from the pip-services3-rpc module), which is needed for the HTTP service to work.

Before we run the microservice, we need to prepare an initial configuration for it. In the config folder, create a config.yml file with the following configuration:

/config/config.yml

---
# Container descriptor
- descriptor: "pip-services:context-info:default:default:1.0"
  name: "beacons"
  description: "Beacons microservice"

# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
  level: "trace"

# Tracer that posts records to log
- descriptor: "pip-services:tracer:log:default:1.0"

# Performance counters that post values to log
- descriptor: "pip-services:counters:log:default:1.0"

{{#unless MONGO_ENABLED}}{{#unless FILE_ENABLED}}
# Memory persistence
- descriptor: "beacons:persistence:memory:default:1.0"
{{/unless}}{{/unless}}

{{#if FILE_ENABLED}}
# File persistence
- descriptor: "beacons:persistence:file:default:1.0"
  path: {{FILE_PATH}}{{#unless FILE_PATH}}"./data/beacons.json"{{/unless}}
{{/if}}

{{#if MONGO_ENABLED}}
# MongoDb persistence
- descriptor: "beacons:persistence:mongodb:default:1.0"
  connection:
    uri: {{MONGO_SERVICE_URI}}
    host: {{MONGO_SERVICE_HOST}}{{#unless MONGO_SERVICE_HOST}}"localhost"{{/unless}}
    port: {{MONGO_SERVICE_PORT}}{{#unless MONGO_SERVICE_PORT}}27017{{/unless}}
    database: {{MONGO_DB}}{{#unless MONGO_DB}}"test"{{/unless}}
{{/if}}

# Controller
- descriptor: "beacons:controller:default:default:1.0"

# Shared HTTP Endpoint
- descriptor: "pip-services:endpoint:http:default:1.0"
  connection:
    protocol: http
    host: 0.0.0.0
    port: {{HTTP_PORT}}{{#unless HTTP_PORT}}8080{{/unless}}

# HTTP Service V1
- descriptor: "beacons:service:http:default:1.0"
  swagger:
    enable: true

# Hearbeat service
- descriptor: "pip-services:heartbeat-service:http:default:1.0"

# Status service
- descriptor: "pip-services:status-service:http:default:1.0"

# Swagger service
- descriptor: "pip-services:swagger-service:http:default:1.0"

Let’s take a closer look at “what’s what” in the configuration above. First of all, the file is structured in such a way that the configuration is divided into sections. Each section consists of one or more descriptors and, optionally, a set of configuration parameters. To keep things organized, a section is meant to configure no more than one component.

In our case, we first configure the context parameters for the container, to make its name and description available. This information will be displayed in the logs as well.

In the next section, the console logger is enabled, and its log level is set to “trace”.

After that, performance counters are enabled and set to send information to the logger.

The next two sections are dedicated to configuring the persistent storage. However, which one we end up using will be determined by whether or not the MONGO_ENABLED environment variable is set. If it is set, then the MongoDB persistence will be used. Otherwise, we will just default to the in-memory persistence

The controller doesn’t need any special configuration parameters, so we just list its descriptor to make sure the component gets created.

To enable our microservice to work on a network, we configure an endpoint with a host + port pair. If the corresponding environment variables are set, then those values will be used. Otherwise, the default values indicated in this section will be used.

The descriptor in the next section creates our HTTP service, which will automatically start listening for requests using the endpoint we configured in the previous section.

The last section configures services that monitor the health and status of our microservice.

Now that we’ve set up the container and a valid configuration, it’s time to move on to the final Step 8. Running and testing the microservice.

Step 7. Running and testing the microservice.