a Microservice Microframework for node; node-xyz (part 2)

This is the second part of my story with node-xyz. It this section, I will focus on the technical aspects of xyz, explaining its architecture and design.

Other parts:

  1. The story of xyz
  2. The technical aspects (this page)

The Main Task

The main focus of the framework is about exposing functions. one microservice should be able to easily expose a function. Another should be able to easily invoke it, without dealing with low level details. It’s should look like this in the end:

workerMs.register('someBusinessLogic', (payload, respond) => {
// calculate some stuff
// fetch some data
respond.jsonify({message: "task done!"})

and the other node, probably located in another host half the way around the globe:

service: 'someBusinessLogic',
payload: {someKeys: 'someValues'},
(err, msgResp) => {
console.log(msgResp) // {message: "task done!"}

Albeit, there is a LOT more to it than this. In order to understand them we should first have glimpse at the architecture.

The Architecture

It was a long long time ago when I decided on the architecture. I honestly don’t even remember why and how I ended up choosing this specific architecture. Nonetheless, I am very glad with it now. This is the outcome:

xyz has a layered structure with configurable middlewares acting as the pipeline conveying instructions and messages between layers.

The module is divided into three layers:

  1. the xyz layer: high level API functions for the developer to use. It technically does nothing other than constructing other layers and calling them with appropriate parameters.
  2. the Service Repository layer: This layer manages local services that its local node is exposing and explores foreign services when it needs to send a message to them.
  3. the Transport layer: a complement for the Service Repository that does the actual job of sending messages over the network and listens for new incoming messages.

It looks something like this:

What gives this architecture its power are the middlewares that connect these layers one another. In a simple form, I can emphasize their importance like this:

No message is allowed to enter or leave without going through middlewares.

I can now complete the last image like this:

Where each white dashed arrow is actually a set of functions inside a middleware stack, not a direct invocation.

The fact that the “functions injected into these middlewares are modifiable by the developer” and that “all messages will go through a middleware” indicates that the entire behavior of the system is also modifiable by the developer. Some examples of this are explained in the following sections.

More than Architecture

There are some other cool aspects about xyz that I personally like. They delineate the power of this architecture and move the framework closer to its objectives.

Transport Independency

Both sending and receiving a message is independent from the underlying network protocol. Each xyz node can have multiple routes for sending messages, each having any arbitrary network protocol. Furthermore, each node can have multiple servers of different types, all receiving foreign messages and sending valid ones up to the service layer and dropping the rest.

Open Access

There are no tricks or attempts to hide internal variables and states from the developer and the documentations strives to explain them all in depth. Indeed, if you don’t need to manipulate them, don’t play around so that you don’t mess things up. But, if you do have a clear purpose, you are more than welcome to hack around all of the three layers and manipulate them.

These two attributes, Open Access and Transport Independency, specifically enabled me to do more decomposition, meaning that more functionality was moved out from the core module. These functionalities were then re-added again as plugins (or what we call Bootstrap Functions). As an example, the service discovery and health checking mechanism was at first fix and built it. After a while, I began to question myself why should it be? As a plugin, it still has all of the required access rights and it brings the benefit of more configurability. As a result, in later versions of xyz, a node could choose to have a specific Service Discovery mechanism, or even choose not to have one. Wondering how might that be useful?

Service Discovery is only useful for when you want to have a dynamic cluster of node. What if for a specific test or application, you know the address of all nodes beforehand and you are sure that they won’t change? Furthermore, different applications with different scale are best to work with different Service Discovery mechanisms. A small cluster of 5 nodes could work with a robust heartbeat based (which is quite inefficient) algorithm that uses reliable HTTP for probing, while this approach is by no mean efficient in a larger cluster and lightweight approach should be used.

Consequently, the current xyz source code is consisted of:

  1. xyz-core, which is basically everything that I just explained.
  2. xyz-cli which will not be explained here but I am sure that you can guess what it is.
  3. numerous middlewares and plugins that used to be a part of xyz-core but now they aren’t.

xyz in Action

Let’s see some examples of how this architecture can be used to achieve new capabilities inside the system.

Changing Transport Layer

One of the features of xyz is that both the transport receiver and sender component are modifiable. Furthermore, each node can choose to have multiple of each. This might be useful in many cases. One simple use case is broadcasting update messages. They have a lower degree of importance, by nature, than the actual function invocation messages. In such cases, nodes can have both HTTP and UDP sender and receiver. They can send important messages to a single node using HTTP, while using the less expensive UDP for their broadcast messages.

Adding Authentication or Encryption

One of the things that xyz proudly doesn’t have is any security. I always state this with emphasis because it conveys a more important concept:

xyz does not enforce any application dependent procedure.

Yes Yes. I know that 99% of the time there should be an authentication for a service . But let’s respect that 1% and decide things like authentication to be optional in xyz.

This can be quite useful because it allows the framework to be used for much wider range of application. You could implement a public service repository in the cloud with xyz, which should not have any strict authentication, while being able to implement an enterprise application with significant authorization roles. How?

Adding features like authentication can be as simple as injecting two middlewares. As said before, everything that is important in xyz happens inside middleware functions. One can simply:

  1. add a function to an outgoing middleware that adds authorization tokens to the message
  2. add a function to a server middleware that checks that authorization token and drops the message if it doesn’t have it.

The output will look like this:

Having a layered architecture makes this feature even more valuable by drawing a clear line between each layer’s responsibility. As an example, in the above scenario, two arbitrary Transport layer middlewares are responsible for adding the token and checking it. The Service layer doesn’t know about this authentication, nor does it care about it. If a message is sent up from the Transport layer to the Service layer it means that it is verified. If it is not, it won’t even reach the Service layer. This separation of concerns makes the development of middlewares clearer. Similar to microservice spec., middleware functions should also do a specific, independent task that can be mixed and composed with other middleware functions.

Adding a Message Queue

Adding a queue is also similar to the authentication approach discussed above. The magic is in the middlewares.

By default messages reach the Transport layer and are sent to the Service layer via a middleware function immediately. Two components are required to easily make xyz cooperate with a queue.

  1. a new middleware function will replace to one that was sending messages to the Service layer. The new one will store messages inside a queue instead of sending them to Service layer.
  2. An independent component will always check the queue for messages and will send them to the service layer. How is it possible? Thanks to the open access policy of the framework. Any external plugin can use the Service Layer, just as the xyz layer that is using it internally.

Send Strategy

Another configurable aspect of xyz is something that I call Send Strategy. Each exposed service in xyz has a path based identifier (like db/user/store or post/add) that other nodes should use when sending a message, if their message has to be delivered to this specific type of service. The so called Send Strategy is a function that should see a message’s destination path, peek the information that it has about other nodes, and decide the IP and PORT of the final destination of this message. These functions can be divided into two categories:

  1. Those who resolve the path of the service: these functions will look at the message and carefully choose a destination node that can respond to it. This is particularly useful when exposed services are more like a Task and offered by multiple nodes, which is not that uncommon. Suppose that we know that five foreign nodes expose a specific task as a function and we just want the message to be sent to one or few of them. We don’t really care about the receiver or the actual physical location of the destination. We just one someone to do this task. Existing Sent Strategies that follow this path are: sendToAll, FirstFind, Round Robin
    These functions will also utilize the fact that the identifier that they are basing their judgment on is a Path, meaning that it can be quite dynamic by allowing wildcards (you can send a message using sentToAll to /aTaskGroup/* which will invoke /aTaskGroup/task1, /aTaskGroup/task2 and /aTaskGroup/task3) or services with deep and well structured paths.
  2. Those who simply ignore that message path and send it according to other parameters. As an example, BroadcastLocal and BroadcastGlobal are strategies that lie in this category.

The aim of allowing this degree of flexibility is to allow the developers to choose the functionalities that they need and ideally, share them with other developers by making their code open-source.

note: some other features that I find cool but won’t explain them here to keep the text short: Round Robin Load Balancing, xyz-cli

Recalling the Objectives

As a conclusion, let’s recall the objectives that I mentioned at the end of part 1 and see how xyz commensurates with them:

Support the portion of the microservice spec. that is required for almost all projects

I think it does enough. Anything more would’ve been application dependent and should be a plugin.

Stay minimal

I think it turned out to be more low level than minimal. The term minimal is quite ambiguous to be honest. But I can see that it actually is low level. A friend of mine, after hearing me present xyz, said that it is more appropriate for writing high level frameworks on top of it than it is for direct development.

Be as clear and lucid as possible

It sure is. If you follow the tutorials, You will immediately get the feeling that you can see all the way through the system. Nothing is hidden. Nothing is a secret. You can sense how messages flow through middlewares and get modified by them until they reach the last middleware of the Transport layer and actually leave your computer.

Be as configurable as possible

Bothe middlewares and bootstrap functions (leveraging from the Open Access attribute) are designed explicitly for this purpose. Theoretically, they are enough to ensure that an application written in xyz can do anything possible. I still haven’t found a counterexample. Drop me a comment if you do!

Use standard javascript. Have minimum dependencies

xyz is written pure by javascript supported by node V6 and above (and can be ported to >4 with minimum effort) no build required. no Babel.

I initially had this objective in mind because even though i absolutely love javascript, I hate the fact that a an overwhelming number of dev tools have to be configured to get things working, only to crash with the next update since everything is moving so fast. I wanted something simple to start with and upgrade it later, if required.

One possible upgrade is that I am currently tempted to draw a red line over this objective and move the source code to TypeScript before it’s too late, mainly because debugging would be easier with strong typings.

Document early and often

Currently, all of the important concepts of xyz (micro)framework are documented in its website. All repositories have sufficient information in their README. the only part that still needs some work is the API Ref.


  1. I only looked at Seneca, the best existing microservice toolkit for node, before starting my development for a few minutes. I was trying to avoid reinventing the wheel by seeing what it has done and trying to do the same. I wanted xyz’s idea to stay pure. About a year later I returned to realize that xyz and Seneca are doing basically the same thing, perhaps with the exact same goal in mind, but with truly different approaches.
  2. Some aspects of xyz are inspired by Akka since I was working with it at the time. Path based services (instead of Pattern Matching) is an example of this inspiration.

The Future Ahead

I will probably keep working on xyz in my spare time as much as possible. It still lacks a lot of features and need more improvements. At the meantime, any suggestion, comment or contribution is kindly welcomed.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.