EWD.js Support For Micro-Services

What Are Micro-Services?

Micro-Services are the latest big buzz in the web and browser-based application development world.  As with many buzzwords in IT, the term is somewhat vague and difficult to specifically pin-point, but it’s worth reading the definitive paper by Martin Fowler on the subject:

http://martinfowler.com/articles/microservices.html

In a nutshell, Micro-Services are all about moving away from the development of huge, monolithic web/browser applications, and instead creating applications that are an assembly of many, small, re-usable components.  It’s all about creating an environment where various, disparate teams in an organisation can focus on just their part of an enterprise, and build the micro-service(s) that others can use in their applications.

The classic example is Amazon’s web site which looks like one large monolithic application to the user, but which is actually an assembly of micro-services for which each department or section within Amazon is individually responsible.  The web page(s) that make up the Amazon site that we, the user, sees, is just a mechanism for pulling together those micro-services in a coherent way.

EWD.js Build 84 (and later) now includes a Micro-Service architecture to allow you to work similarly.  It actually allows you to create and work with two kinds of Micro-Services:

  • back-end Services
  • front-end Services

An EWD.js application may use either or both types.

Back-end Micro-Services

These are the simplest and easiest to understand and use.  The idea is that instead of an EWD.js application having a single back-end module that contains all of its message handlers and associated back-end business logic, the EWD.js application can also make use of one or more back-end service modules, each of which focus on a particular functional or business area.  For example, your application may require an assembly of back-end services for:

  • accounts
  • personnel
  • stock control
  • billing
  • customer support
  • etc

A back-end Micro-Service is simply a set of message handlers, but it can be written in isolation and used by any EWD.js application that needs it.

For example, here’s a simple back-end example Service:

 
module.exports = {
 onMessage: {
   backEndServiceMessage: function(params, ewd) {
     return {status: 'Success!'};
   }
 }
};

It is saved in the ~/ewdjs/node_modules directory along with all other back-end modules.  Suppose we save it as exampleService.js (You can name it anything you wish, but it’s a good idea to use a meaningful name that describes the functional area it covers, eg accounts.js, stockControl.js etc).

To use this Micro-Service in your application, you simply add the following to its back-end module:

 
module.exports = {
  onMessage: {
     // your application’s own specific message handlers
  },

  services: function() {
    // return an array of micro-services that this application
    // is allowed to use
   return ['exampleService']; 
  }
};

This tells EWD.js the list of back-end Micro-Services that our application is allowed to use.    Each name in the array must match the name of a back-end Micro-Service module (without the .js file extension).  This services() function is important because we need to prevent a malicious user attempting to send rogue messages that attempt to use Micro-Services that are not normally available to the application!

Now our application’s front-end app.js file can send messages to the exampleService Micro-Service module, simply by adding a new property named service to the EWD.sockets.sendMessage() function’s input object:

 
      EWD.sockets.sendMessage({
        type:'backEndServiceMessage',
         service: 'exampleService',
        params: {
          // message payload
        },
        done: function(messageObj) {
          // handle the response from the handler in 
          // the exampleService Micro-Service
        }
      });

If our EWD.js application is named myApp, then instead of this backEndServiceMessage message being handled by its back-end module myApp.js, it is handled by the corresponding handler in the back-end Micro-Service module named exampleService.js instead.

Of course, if you don’t specify a service property in an EWD.sockets.sendMessage() function call, it will behave as normal and try to use a message handler in the application’s own back-end Module (in this case myApp.js)

Front-end Micro-Services

EWD.js now also allows you to define a set of actions and message handlers that can be used by the front-end of any EWD.js applications.  Just as with back-end Micro-Services, the idea is that the author of such a front-end Micro-Service does not need to know anything about the EWD.js applications it will be used in, so they are completely re-usable.

A front-end Micro-Service may also optionally include a fragment file that can be loaded into an EWD.js application’s index.html, but fragments can alternatively be tailor-made by the application author(s) to ensure that they are correctly structured and styled to fit with the design of the overall page.

A front-end Micro-Service may be designed to focus on handling a particular piece of business functionality (eg how to handle stock-control listings), or they may focus on one or more UI widgets (eg a generic calendar or grid control).  They are a way of creating re-usable front-end behaviour and functionality.

Front-end Micro-Services are somewhat more complex than Back-end Micro-Services, but the trick to using them is to make use of their standard pattern.  Use the examples below as templates that you subsequently modify and extend.

When you install EWD.js build 84, you’ll find a new directory under ~/ewdjs/www:

      ~/ewdjs/www/services

This is where you should file all your front-end Micro-Service modules and their associated fragment files.  You’ll find an example one in there: demoPatientProfile.js and its associated fragment file demoPatientProfile.html 

The general pattern of a front-end Micro-Service module file is as follows:

 
define([],function() {

  // put any private functions in here

  return {

    init: function(serviceName) {
      // anything here is run when the service is loaded by EWD.require()
    },

    fragmentName: 'main.html', // optional, overrides automatic fragment path

    onMessage: {
      // message handlers for front-end service messages
    },

    onFragment: {
      // optional additional fragment load handlers
    }
  }
});

A front-end Micro-service is loaded into an application’s browser by using a new function: EWD.require().  This makes use of the require.js JavaScript module loader (http://requirejs.org).  A typical use of EWD.require() is shown below.  This would be invoked from within your EWD.js application’s app.js file:

 
EWD.require({
  serviceName: 'patientProfile',
  targetSelector: '#main_Container',
  done: function() {
    console.log('done!');
  }
});

serviceName specifies the name of the front-end Micro-service you want to load.  EWD.js will look for a file of the same name in your /services directory with a file extension of .js – so in the example above it will load /services/patientProfile.js

targetSelector specifies the target HTML element(s) into which the service’s fragment will be loaded.  If a simple string value is used, EWD.js will load the fragment into the element that has an Id of that value.  Alternatively you can use a jQuery selector, in which case the fragment will be loaded into element(s) that match the specified jQuery selector.  So, in the example above, the service’s fragment will be loaded into the element with an id of mainContainer

fragmentName may or may not be specified.  If not specified (as in the example above), EWD.js will attempt to load a file of the same name as the serviceName with an extension of .html from your /services directory – so in the example above it will load /services/patientProfile.html  If fragmentName is specified, EWD.js will attempt to load a file of the name you specify, with an extension of .html, from the application’s own directory (ie /www/ewd/{applicationName}.   You can optionally suppress the loading of the service’s fragment by specifying fragmentName: false

When a front-end Micro-service is loaded using EWD.require(), four things happen in strict (synchronous) sequence:

  • the fragment is loaded
  • the corresponding onFragment() handler, if defined in the application’s app.js file, fires
  • the service’s init() function fires
  • the done() function in the EWD.require(), if defined, fires

Typically you’ll use each handler as follows:

  • in most circumstances the application developer won’t need to provide an onFragment() handler.  However, if the developer understands the service in detail, he/she can provide an onFragment() handler to customise the fragment, modify its markup, extend its handlers etc.
  • the service’s init() function is where the service author invokes code that defines the behaviour of the service in terms of handlers, events etc.  Note that the init() function has access to any private functions that are defined at the top of the service module.  The init() function may also be used to extend EWD.application with extra methods that are then available to the app,js of any EWD.js application that loads the service.
  • the EWD.require()’s done() function is where the application author (ie the user of the service) can optionally invoke code specific to the application’s use of the service.  By the time this code runs, the fragment is fully loaded and the service is initialised.  We’d anticipate that the done() function can be ignored for most services, and should only be used by advanced users who understand in detail the service they are loading.

If the application author decides to use his/her own fragment instead of one provided by the service (if any), then it is the application author’s responsibility to provide the onFragment() handler for it.  Once again, we’d only anticipate advanced users who fully understand the workings of a service to specify their own fragment.  Note that doing so will not otherwise affect the event sequence described above.

Demonstration Example

If you install or upgrade to EWD.js Build 84, you’ll find a demonstration example in ~/ewdjs/www/ewd/demoMicroServices.  You can run this by using the usual URL:

      http://{ipAddress}:8080/ewd/demoMicroServices/index.html

You should see a page coming up with some patient profile details.  What’s actually happened is that the profile content page has come from a front-end Micro-Service, populated with data via a back-end Micro-Service.

Take a look in the file ~/ewdjs/www/ewd/demoMicroServices/app.js.  In its onStartup() handler, you’ll see where it loads the front-end Micro-Service named demoPatientProfile:

 
    EWD.require({
      serviceName:'demoPatientProfile',
      targetSelector:'#main_Container',
      done: function() {
        console.log('app.js: demoPatientProfile service loaded successfully');
      }
    });

You’ll find this Micro-Service in ~/ewdjs/www/services:

  • its fragment is named demoPatientProfile.html
  • its logic is in demoPatientProfile.js

Because fragmentName isn’t specified in the EWD.require(), the fragment is automatically loaded and the targetSelector property in the EWD.require() tells EWD.js to inject it into the element whose Id is main_Container.

Now take a look inside demoPatientProfile.js and find its init() function. 

 
    init: function(serviceName) {
      console.log('demoPatientProfile service: init() firing');
      // lets call upon the profile back end service now
      EWD.sockets.sendMessage({
        type:'getUserData',
        service:'demoPatientProfile',
        frontEndService: serviceName
      });
      console.log('demoPatientProfile service: message sent to demoPatientProfile back-end service');
    },

You’ll see that it sends a message to the back-end with a type of getUserData.  However, notice the service property of demoPatientProfile.  This means it’s accessing a back-end Micro-Service named demoPatientProfile.

Take a look at the back-end module for our demoMicroServices application: ~/ewdjs/node_modules/demoMicroServices.js:

 
module.exports = {
  onMessage: {
  },
  // authorise this app to access the demoPatientProfile back-end Micro-Service
  services: function() {
   return ['demoPatientProfile'];
  }
};

Because the front-end Micro-Service used by our demoMicroServices application is using the back-end Micro-Service named demoPatientProfile, our application must first be authorised to use it: that’s done in the services() function shown above.

Now take a look at the back-end MicroService in ~/ewdjs/node_modules/demoPatientProfile.js:

 
module.exports = {
  onMessage: {

    // returns some patient info
    // this likely would have been fetched from our database
    getUserData: function(params,ewd) {
      var patientData = {
        'First Name': 'Patient',
        'Last Name': 'Zero',
        'Date of Birth': '11/11/1950',
        'Address': '10 The Lane',
        'City': 'London',
        'State': 'UK'
      } 
      return patientData;
    }
  }
};

This looks just like is a standard back-end module.  Its response is returned to the front end, but instead of being handled by the application’s app.js, it’s handled inside the demoPatientProfile service.  To understand why, look again at how it sent that message to the back-end Micro-service:

 
     EWD.sockets.sendMessage({
        type:'getUserData',
        service:'demoPatientProfile',
        frontEndService: serviceName
      });

Notice the frontEndService property set to the value of serviceName – this tells EWD.js that the handler for this message’s response is within this front-end Service.  Further down you’ll find that handler:

 
     onMessage: {
      // handle the profile data our init() method has fetched
      getUserData: function(messageObj) {
        console.log('demoPatientProfile service: response received from dempPatientProfile back-end service');
        var patientData = messageObj.message;
        populateProfileData(patientData);
        $('.js-profile').show();
      }
    }

This populates the fragment and displays it.  That’s the service completed, and the done() function in the EWD.require() that loaded the Micro-Service will now fire:

 
   EWD.require({
      serviceName:'demoPatientProfile',
      targetSelector:'#main_Container',
              done: function() {
                console.log('app.js: demoPatientProfile service loaded successfully');
            }
    });

Use of the done() function is optional: we’re using it here just to display a log message in the console to confirm the end of the sequence of events.  We could have left it out completely.

That’s it!  Our application has successfully loaded and used a front-end and back-end Micro-Service.  The interesting thing is that both the front-end and back-end Micro-Services were written without any knowledge of how they would be used within the application, and any application developer can simply load our front-end Micro-Service using EWD.require() and treat it like a “black-box”.   

Try running the application again, this time with the browser’s JavaScript console open.  You’ll see a whole series of log messages being written out: they’ve been deliberately added to the demo application and front-end Micro-Service.  You can find them within the various JavaScript files and confirm for yourself how the sequence of events takes place.

Conclusion

So that’s the EWD.js take on Micro-Services.  They are a very powerful concept, and will allow true team development across the Enterprise and code re-use.

I’ll be interested to hear how people begin to use Micro-Services with EWD.js – all comments and feedback welcome!

Advertisements

5 comments

  1. The micro service looks very much like a promise API… All that appears to be missing is a handler if the data is not available. So if the request fails (or fails within a given time frame) a companion function to “done” (say, “failure”) is populated and can fire a client side response.

    Am I making any sense here? This appears to be exactly what was needed to allow EWD to be a native data provider within Angular.js controllers and services.

  2. Important “safety tip”: At least in my case, editing the “services: ” method required a shutdown of ewd.js and a restart in order to take.

    Otherwise…this is about as cool as it gets.

    jb

  3. Rob, this indeed a very powerful extension to EWD. One can structure the application much better now by breaking up a large app into smaller parts. Using this new feature in your application is also surprisingly easy, especially for back-end services.

  4. I think you are mixing up microservices and module pattern here?

    Microservices by definition should be ran in their own process (so they can be scaled separately) and by convention interface over HTTP (so they can be run transparently either locally or remotely from the caller).

    1. In fact, at the time of writing this article (a year ago), microservices were a pretty new concept, the definition of which was fairly vague. The most important aims of microservices were, however, clear at that time:

      – to allow teams of developers within different parts of an enterprise to develop services specific to their areas of expertise in isolation from other teams

      – to allow those services to be easily and seamlessly consumed by multiple applications – each application would be a particular assembly of microservices from the relevants parts of the enterprise.

      The EWD.js microservice architecture achieves these objectives.

      If, in the meantime, the industry has subsequently determined that the primary aim of microservices is a means of scaling out physical application infrastructure, then, in my opinion, that’s an unnecessarily limiting interpretation of what they’re for.

      Nevertheless, there would be no reason why a back-end EWD.js micro-service module couldn’t be a stub that invokes calls over HTTP(S) to remote web services that do the actual work, therefore meeting this new objective of microservices also.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: