Talk is cheap, show me the code

I believe I have made clear the case for true microservices. Now let’s see how to implement these in real life, with real code.

Let’s get started

We will be implementing our microservices in Node.js with a library called cote, which is a Node.js library for building zero-configuration microservices applications. It’s available as an npm package.

Install cote via npm:

npm install cote

Using cote for the first time

Whether you want to integrate cote with an existing web application — e.g. based on express.js as exemplified here — or you want to rewrite a portion of your monolith, or you want to rewrite a few of your microservices with cote, all you do with cote is to instantiate and make use of a few of cote’s components (e.g. Responder, Requester, Publisher, Subscriber) depending on your needs. These components are designed to communicate with each other to realize the most common scenarios in application development. While one component per process might be enough for simple applications or for tiny microservices, a complex application would require close communication and collaboration of multiple components and multiple microservices. Hence, you may instantiate multiple components in a single service/process/application.

We will start with implementing our client in this application, which we may conveniently call conversion-client.js . It shall ask for certain currency conversions to be made, and act upon the response.

Implementing a request-response mechanism

The most common scenario for applications is the request-response cycle. Typically, one microservice would request a task to be carried out or make a query to another microservice, and get a response in return. Let’s implement such a solution with cote.

First, require cote;

It’s your regular old library require call.

Creating a requester

Let’s start with a Requester that shall ask for currency conversions. Requester and all other components are classes on the cote module, so we instantiate them with the new keyword.

All cote components require an object as the first argument, which should at least have a name property to identify the component. The name is used mainly as an identifier in monitoring components, and it’s helpful when you read the logs later on as each component, by default, logs the name of the other components they discover.

Requester s send requests to the ecosystem, and are expected to be used alongside Responder s to fulfill those requests. If there are no Responder s around, a Requester will just queue the request until one is available. If there are multiple Responder s, a Requester will use them in a round-robin fashion, load-balancing among them.

Let’s create and send a convert request, to ask for conversion from USD into EUR.

You can now save this file as conversion-client.js and run it via node conversion-client.js .

Here’s the whole conversion-client.js as a reference:

The complete conversion-client.js file

Now this request will do nothing, and there won’t be any logs in the console, because there are no components to fulfill this request and produce a response.

Keep this process running, and let’s create a Responder to respond to currency conversion requests.

Creating a responder

We first require cote and instantiate a Responder with the new keyword.

Each Responder is also an instance of EventEmitter2 . Responding to a certain request, let’s say convert , is the same as listening to the convert event, and handling it with a function that takes two parameters: a request and a callback. The request parameter holds information about a single request, and it’s basically the same request object the requester above sent. The second parameter, the callback, expects to be called with the actual response.

Here’s how a simple implementation might look like.

Now you can save this file as conversion-service.js and run it via node conversion-service.js on a separate terminal.

Again, a complete conversion-service would look like the following:

The complete conversion-service.js file

As you run the service, you will immediately see the first request in conversion-client.js being fulfilled and logged to the console. Now you can take this idea and build your services on it.

Notice how we didn’t have to configure IP addresses, ports, hostnames, or anything else.

Congratulations, you’ve completed your first set of microservices!

Now in separate terminals, you can run multiple copies of each service, and you will see that everything works perfectly. Stop a few conversion services, restart them, and you will see how this system effectively meets the 5 requirements of modern microservices. And if you want to scale out across machines and datacenters, you can either use your own infrastructure or use Docker to handle the breadth of the task of managing infrastructure.

Pushing forward: Tracking changes in the system with a publish-subscribe mechanism

One of the benefits of a microservices approach is its ease of use as a tool for tasks that previously required serious infrastructural investments. Such a task is managing updates and tracking changes in a system. Previously, this required at least a queue infrastructure with fanout, and scaling and managing this technological dependency would be a hurdle on its own.

Fortunately, cote solves this problem in a very intuitive and almost magical way.

Say, we need an arbitration service in our application which decides currency rates, and whenever there’s a change within the system, it should notify all the instances of conversion services, so that they facilitate the new values.

The keyword here is “notifying all the instances of conversion services”. In a highly-available microservices application, we would have several conversion services sharing the load of your application. When there’s an update to the currency rates, these services should all be informed about the change. If there were only one conversion service, this could easily be achieved via a request-response mechanism. But since we want to be free to decide how many copies we run simultaneously, we need a mechanism that notifies every single conversion service, all at once. In cote, this functionality is achieved via publishers and subscribers.

Of course, the arbitration service would be API driven, and would receive the new rates over another request so that for example an admin can enter the values through a back-office application. The arbitration service should take this update and basically forward it to every conversion service. In order to achieve this, the arbitration service should have two components: one Responder for the API updates and one Publisher for notifying the conversion services. In addition to this, the conversion services should be updated to include a Subscriber . Let’s see this in action.

Creating the arbitration service

A simple implementation of such a service would look like the following. First, we require cote and instantiate a responder for the API. Now, there is a small, but important way of building microservices with cote. Due to its zero-configuration nature, every Requester in the system will connect to every Responder it discovers, regardless of the request type. This means that every Responder should respond to the exact same set of requests, because Requester s will load-balance requests between all connected Responder s regardless of their capabilities, i.e, whether or not they can handle a given request.

In this case, we want to create a Responder for a different set of requests. This means we need to differentiate this from our regular services which exchange convert requests. In cote, this is done by simply defining a key for a component. Defining keys is the easiest way to regulate service communication. Here’s how we would create the said Responder .

Now we need a mechanism to keep track of currency rates in our system. Let’s say we keep them in a local variable at the module scope. This could just as well be a database call, but for the sake of simplicity let’s keep this local.

Now the responder shall respond to an update rate request, allowing admins to update it from a back-office application. The back-office integration isn’t important at this moment, but here is an example how back offices could interact with cote responders in the backend. Basically, this service should have a responder to take in the new rates for a currency exchange.

As an exercise, you can create a cote requester to, for example, make the update rate call periodically and vary the rate. Call this file arbitration-admin.js and make sure to incorporate setInterval to demonstrate variation in conversion rates.

Creating a publisher

We now have the rates, but the rest of the system, namely, the conversion services aren’t aware of this change yet. In order to update them of the changes, we should create a Publisher .

Now whenever there’s a new rate, we should utilize this Publisher . The update rate handler thus becomes:

With the publish functionality implemented, here is the complete arbitration-service.js file:

The complete arbitration-service.js file.

Since currently there are no subscribers in this system, nobody will be notified of these changes. In order to facilitate this update mechanism, we need to go back to our conversion-service.js and add a Subscriber to it.

Creating a subscriber

A Subscriber is a regular cote component, so we instantiate it with the following:

Put this line in conversion-service.js .

Subscriber also extends EventEmitter2 , and although these services might run in machines that are continents apart, any published updates will end up in a Subscriber as an event for us to consume.

Update conversion-service.js and add the following to listen to updates from the arbitration service.

That’s it! From now on, this conversion service will synchronize with the arbitration service and receive its updates. The new conversion requests after an update will be done over the new rate.

So far we have created three services that work together to realize a currency conversion system based on microservices. For a recap, I have created a repository on GitHub that incorporates all these services plus an automatic arbitration updater. Feel free to clone and play with it.

Conclusion

This article barely scratched the surface of microservices. It should be taken as an introduction to proper microservice architectures, where we employ similar approaches in larger scales. There’s also a lot of exciting features in cote that are not covered here.

I believe we are on the brink of very interesting and exciting times with respect to how we structure our software, and I strongly support the simplification of our methods. cote is an effort in that regard and it’s only the beginning. It’s already time to enable the world of microservices for everyone.

Further steps

Take a look at our GitHub repository, and join our Slack community if you want to experiment with cote. We are also looking for active contributors, so let us know if you are interested in taking on the challenge of making microservices accessible.

As an advanced, all-in example, we have another GitHub repository that showcases a simple e-commerce application implementation with cote. The example gives you the following out of the box;

a back-office with real-time updates for managing the catalogue of products and displaying sales with a RESTful API (express.js)

a storefront for end-users with real-time updates to products where they can buy the products with WebSockets (socket.io)

a user microservice for user CRUD

a product microservice for product CRUD

a purchase microservice that enables users to buy products

a payment microservice that deals with money transactions that occur as a result of purchases

Docker compose configuration for running the system locally

Docker cloud configuration for running the system in Docker Cloud

If you wish to experiment with cote in Docker and Docker Cloud, I also have a webinar recording which is a step-by-step guide from zero to running cote microservices in production scale with continuous integration:

And that’s it! Thanks a lot for reading this article, and please share your thoughts in the comments.

If you liked this, click the 💚 below to show your support and spread the word.