In my previous article, I talked about how to choose Microservice or Monolith for your new project.
As an architect may you follow the points that I mentioned in the previous article and come to a conclusion that you will going to use Microservice Architecture, a big cheers for you. You promoted yourselves as a first-class citizens of the new era of Digital world, but what’s next? You have heard that Microservice Architecture demands Componentization of service but what is actually a component in Microservice world?
In this article, I briefly discuss what does Componentization mean, and when we need to do Componentization of a Utility module, what problems we face.
Componentization of service:: By the word ” Microservice“, we can understand it is a suite of small services, so the main objective is breaking a project into multiple services. But what does it mean by services, which kind of services are those? Are we talking about a Service layer in a layered architecture, or a service wrap into the jar so-called “Library” or publish it through REST API?
To understand always remember ” Microservices means an Independent thing (Component/Service whatever you call it) that can be deployed independently and update independently managing lifecycle independently“.
When you Create a Microservice there should not be any confusion. Maybe many tricky situations come but stick to basics “Microservice mean an independent deployable thing“.
One of the best dilemmas is: Suppose you have a utility module. Now you are in a confusion what should you do, wrap utility module into a jar and all other Miocroservice use the same as a jar , so that there should not be a code duplication or expose a utility service with API fiction(Separate Microservice) so others can consume it?
One may think, making utility module as a jar seems severely wrong in Microservice workspace, as Microservice means several independent small services so Utility module should be published as a service and Utility module must be a Microservice.
Why is the Utility module as a Jar a bad Idea?
When you import utility module as a jar you limit yourself. Now your service is dependent on utility module which packaged as jar. If utility modules java version upgrade you have to upgrade your service unless your program is not able to use that jar. Now you both should stick in Java (Although once service is written in one language, I saw rarely that is rewritten in another language). Having said that, you should not confined your Microservice to a language (Java). Think your utility module is used by many microservices as a jar, so if you want to upgrade your version it should be backward compatible. Lets say that you want to create functions which can easily be achieved by Java9 but you can not upgrade your utility as other microservices not upgraded to Java9.
Why Utility module as a Jar is a good Idea?
As a counter logic many can argue, to build a Microservice itself, we import jars/Libraries like Sprint boot starter parent, or GSON, Jackson etc. So why we should not package our utility as jar and why it is a bad idea? Many architect think it is a brilliant idea as it solves many purposes.
If we are not using utility module as a jar then we have two options:
1. Duplicate the functionality of a utility module in all Microservices.
2. Create a Microservice called Utility service and publish an API to invoke utility methods.
These two options have their own demerits. Duplicate the functionality of utility module in all Microservices::Here the objective duplicate the code to all Microservices who consumes Utility module, so that there is no utility module as such. All are the part of local codebase, but it is against the DRY principle and it is a bad idea. If the utility module is holding some complex code which associates many classes, that it is been copied to all Microservices and any future changes in that, has to be copied to all Microservices. So there is a problem of maintainability.
Say we have a utility module which calls an External service and get a complex response. This module parsing the responses apply some business logic on it and create some analytical data which exposes public methods (Java API). Now if you copied that complex algorithm written in Utility module in to multiple services certainly it is a very bad idea,. Think how many time duplication has been done, and if now a new analytical data is needed you have to add it in all Microservices . Oops what a pain while I am writing this I am getting feared to imagine the scenario. So certainly it does not work unless you have a very small portion of utility code and which is not changeable. Lets say a generic code which deals with Date, TimeZone, and Format, ( A single class with multiple static methods). Copying that class in all Microservice is a onetime effort.
1. Duplicate the functionality of utility module in all Microservices::Here the objective duplicates the code to all Microservices who consumes Utility module, so that there is no utility module as such. All are the part of local codebase, but it is against the DRY principle and this is bad idea. If the utility module is holding some complex code which associates many classes, unnecessary that has been copied to all Microservices and any future changes in that, has to be copied over all Microservices. So there is a problem of maintainability, say we have a utility module which calls an External service and get a complex response then this module parsing the responses apply some business logic on it and create some analytical data which exposes as public methods (Java API), Now if you copied that complex algorithm written in Utility module in to multiple services certainly it is a very bad idea, think how many time duplication has been done, and if now a new analytical data is needed you have to add it in all Microservices , oops what a pain while I am writing this I am getting feared to imagine the scenario. So certainly it does not work unless you have a very small portion of utility code and which is not changeable say a generic code which deals with Date, TimeZone, and Format, ( A single class with multiple static methods), Copying that class in all Microservice is a onetime effort.
2. Create a Microservice called utility service and publish an API to invoke utility methods :
So, you create one Microservice where all utility methods are dumped, as this is a Utility module all Microservice or most of the Microservice Communicate with it. Every Microservice has a link with this Microservice. Just imagine the picture every Microservice dependent(HAS-A) on it. If that service is down due to some erroneous code or all instances down due to some major causes, all service will be down Total Microservice architecture is doomed then how it is different than a monolith?
As per Microservice, partial failure may happen but total Microservice will not down ever. It has 100% uptime.
So certainly creating a Utility Microservice not looking very promising.
As an Architect What should you do now It is like a double-edged sword whatever the option you choose you have to deal with demerits.
Taking a decision :: As an architect, we always dealt with Demerits and try to choose in which case, which one to choose so it has least demerits. So, as an architect our favourite answer is “It depends on scenario”, and we are hated for that answer. Even Juniors are mocking us and they do not have direct answer always.
Here I also giving an answer in a same way, It depends :) How your utility method is written, in a one liner if your utilty functions are statless use Jar , If your utility functions are dealing with State use Microservice.
If you observe minutely, you can divide your utility methods into two categories.
One type of utility modules takes and input doing some operation on it and returns a result so there are no side effects and it is independent of any parameter state, every time you pass the same parameter you got the same result. In this case do not publish those as separate Microservice, as this is not dealing with state , so no need to publish as REST resource, because no create, Put ,delete operation is done by this. So, you can package this as a jar and use in every microservice. Think about Utility jars like Jackson, Gson, they take request to do some operation that returns some data structure. But if your utility takes and request fetch some data from database, do some operation and return it or save that state in database. Then it would be ideal to publish it as Microservice.
Here also think about the failure,. If you make a synchronous operation and your utility module fails this means all chain of Microservices is failed. So think that you make utility operation as async, is the utility module require in sync fashion? As an example Event store or store Audit information, those are cross-cutting concerns and a utility operation it is not a part of main business flow so we can make that async. So that if audit information or even storing operation fails it does not block the business flow and your service should not down for it.
If you have to use utility operation in sync fashion, then implement a circuit breaker and return a default path so that if the operation fails it does not block all Microservices. At least if a default path appears and shows the user’s message. Users can perform other operations rather that this function (Say your transaction function calling a utility function to check Transaction amount is based on that bank giving you some credit points. Now if that function fails we can show a default message “at this time we can’t process the transaction” but user can do other options like balance check. So any moment of time the service will not be totally down.
Conclusion: There are also different shades of Utility. Some are read-only but have database operation and some may use in-memory caching. So take a decision wisely on how you want to write your utility one: jars or Microservice.
Published on Java Code Geeks with permission by Shamik Mitra, partner at our JCG program. See the original article here: Dilemma on Utility module , making a jar or separate Microservice?
Opinions expressed by Java Code Geeks contributors are their own.