To Introduction

Microservice Facade

Step 2. Business operations

Since facades are usually the point of entry into a system, they can contain dozens or even hundreds of REST operations. The classic microservices structure, when all the logic is contained in a single controller, becomes quite impractical in this case. Furthermore, it’s critical for a facade to support versioning. When the interface is changed, the facade must continue to provide stability for existing clients using interface versioning. Usually around 80% of the logic remains the same when an interface is changed, so duplicating the logic would just increase the amount of code and make it more difficult to support.

To solve these problems, the Pip.Services Toolkit offers a new pattern that breaks up logic into separate operations. The operations can be developed and tested individually, and then integrated into the RESTful service using unique routes. When implementing a new version, only the operations that require changes need to be rewritten. The remaining operations are simply imported from the old version by being reregistered in the new RESTful service.

The example facade in this tutorial will contain just 2 sets of operations:

  • Operations that work with Beacons
  • Operations for managing sessions and users

We’ll be creating a separate file for each set of operations and placing them in the folder operations/version1

Let’s start with the first set of operations - the ones responsible for working with Beacons.

Create a file named BeaconsOperationsV1.ts and place the following code inside:

/src/operations/version1/BeaconsOperationsV1.ts

let _ = require('lodash');
let async = require('async');

import { IReferences } from 'pip-services3-commons-node';
import { Descriptor } from 'pip-services3-commons-node';
import { RestOperations } from 'pip-services3-rpc-node';

import { IBeaconsClientV1 } from 'pip-clients-beacons-node';

export class BeaconsOperationsV1  extends RestOperations {
   private _beaconsClient: IBeaconsClientV1;

   public constructor() {
       super();
       this._dependencyResolver.put('beacons', new Descriptor('pip-services-beacons', 'client', '*', '*', '1.0'));
   }

  public setReferences(references: IReferences): void {
       super.setReferences(references);
       this._beaconsClient = this._dependencyResolver.getOneRequired<IBeaconsClientV1>('beacons');
   }

   public getBeacons(req: any, res: any): void {
       let filter = this.getFilterParams(req);
       let paging = this.getPagingParams(req);
       this._beaconsClient.getBeacons(
           null, filter, paging, (err, page) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(page);
                   this.sendResult(req, res);
               }
           }
       );
   }
   public getBeaconById(req: any, res: any): void {
       let id = req.route.params.id;
       this._beaconsClient.getBeaconById(
           null, id, (err, item) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(item);
                   this.sendResult(req, res);
               }
           }
       );
   }
   public getBeaconByUdi(req: any, res: any): void {
       let udi = req.route.params.udi;
       this._beaconsClient.getBeaconByUdi(
           null, udi, (err, item) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(item);
                   this.sendResult(req, res);
               }
           }
       );
   }
   public createBeacon(req: any, res: any): void {
       let data = req.body;
       this._beaconsClient.createBeacon(
           null, data, (err, item) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(item);
                   this.sendResult(req, res);
               }
           }
       );
   }
   public updateBeacon(req: any, res: any): void {
       let data = req.body;
       this._beaconsClient.updateBeacon(
           null, data, (err, item) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(item);
                   this.sendResult(req, res);
               }
           }
       );
   }
   public deleteBeaconById(req: any, res: any): void {
       let id = req.route.params.id;
       this._beaconsClient.deleteBeaconById(
           null, id, (err, item) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(item);
                   this.sendResult(req, res);
               }
           }
       );
   }
   public calculatePosition(req: any, res: any): void {
       let orgId = req.route.params.orgId || req.route.params.siteId;
       let udis = req.route.params.udis;
       if (_.isString(udis))
           udis = udis.split(',');
       if (!_.isArray(udis))
           udis = null;
      this._beaconsClient.calculatePosition(
           null, orgId, udis, (err, position) => {
               if (err) {
                   this.sendError(req, res, err);
               } else {
                   res.json(position);
                   this.sendResult(req, res);
               }
           }
       );
   }
}

This component’s logic is based on calling the Beacons microservice via any client that implements the IBeaconsClientV1 interface. The component receives a link to the client through its SetReferences method (see [The Component References recipe]). The component’s business methods mainly just wrap the client’s methods to convert facade’s RESTful requests into calls to the client. Generally speaking, all of these methods perform the same set of steps: extract parameters from the request, call the corresponding method in the Beacons client, receive any results or errors, and send this information back as a response.

In the next (third) Step - Authentication and session operations - we’ll be examining the second set of operations, which manage sessions and authenticate users.