Ever since microservice-based architecture was introduced in the early 20th century more and more developers and organizations have been adopting this methodology to build hyper scalable applications. A pioneering organization such as Netflix has pointed to their microservice-based architecture as one of the key enablers for their ability to scale, and they have been fortunate to have been following the microservice methodology from their very beginning. As the methodology is gaining more and more popularity amongst developers, many tech companies now find themselves in a position where they have to migrate their "old" monolithic applications to a scalable microservice-based application.
An example of this can be an on-premise software provider now wanting to offer their software in the cloud and as a service. This means that they need to migrate all distributed on-premise installations into one centralized and managed multitenant cloud solution. It now becomes evident that their monolithic design (which has been working for years when installed on-premise) cannot scale when centrally hosted by the company itself. They will have to migrate the application to a microservice-based design.
At KONCIV, as our platform grows, we are continuously faced with the challenge of core services building in size and complexity and need to be migrated to separate services. In this article, we wanted to share our thought on migrating to a microservice-based architecture.
When migrating from monolithic to microservice we need to be very intentive of the end goal, the priority, and ultimately the cost.
In the race to keep up the pace, it is often difficult to judge when to migrate your application or a large service to microservices and what advantage it can add. Naturally, there is a higher upfront cost of prioritizing such a migration, with rarely any short term benefit to the business. The issue with this is that a correct microservice-based architecture is not being prioritized in front of other business requirements that can yield short term results.
The above is an obvious management perspective and a thought process, but it is the developers responsibility to highlight the long term business value by prioritizing such a migration. Although benefits might be obvious to the technical management, it is often more difficult for the business end to understand what value is realized. So the decision to migrate monoliths to microservices needs to be firmly anchored in the senior management group before starting. Unless you might end up with a half- baked result - a distributed monolith.
So how do we as developers/development team see the challenge, what key factors should we keep in mind, and what cloud design patterns do we choose?
Characteristics of microservices
As mentioned, microservices has been a very popular methodology, but strangely enough not so well-documented. Naturally, there are articles available (and a lot of blog posts), but as different organization defines individual goals for their microservice implementation this will govern the approach rather than jargon floating around on the internet. However, there are some key concepts worth mentioning that you need to be faithful to.
1. No single point of failure. The service should be able to run independently with no (or as little as possible) dependencies on other services.
2. Services should have the capability to scale independently without affecting the other services.
3. Pay extra attention to database design, isolate the database layer from each other. You know what part of the database/table/documents is growing exponentially compared to others.
4. Make sure there is no bottleneck. Understand user traffic, trim down the bottleneck. In most cases, Authentication/Authorization service becomes a pain point.
5. Make sure you do not overload a specific service. While migrating from a monolith to microservices, the most challenging part is to divide into smaller and (most importantly) equal size. Often this depends on the components inner complications and usability. However, this might just be the most critical decision you will make as overloading specific services will ultimately be "as bad" as just having a monolithic design. Thus, very little benefit is realized by implementing microservice-based architecture.
Although microservice-based architecture is, as mentioned, increasing in popularity and has definitely proven its value the (maybe) most important question you need to ask yourself is (although this contradicts this article):
Do you really need microservices?
A consequence of using services as components is that the application needs to be designed so that they can tolerate the failure of independent services. As any service call could fail due to the unavailability of the supplier, the client has to respond to this as gracefully as possible. This is a disadvantage compared to a monolithic design as it introduces additional complexity. So carefully consider this as introducing a microservice-based design will increase the complexity of development and thus increase development time. The benefit is naturally scalability and reliability, but be true to yourself and your organization if this is actually required for the service or application you are evaluating.
And also, unless you can draw a stable version of most of the micro-services it will be difficult to manage the microservices architecture for long because as an individual or as a team it will be difficult to manage the break/fix operation for multiple services. Again, microservice-based architecture is complex! But using a standardized scrum development cycle and appropriate TTD you can manage this. We recommend using Mockito - cloud contracts to define the fine line of interoperability between the services. But it is evident now that microservice-based architecture is far more than just a design pattern - it impacts the entire development process.
A tree falls the way it leans. ~ Walloon Proverb
Where to start?
So when you decide to migrate to a microservice-based design, where should you start? We usually see developers start with the config server, followed by service discovery/registry and slowly migrate based on the rest API segregation ( /user will go to user-service /employee will go to employee-service), alas this is not a necessarily the best practice as ( before it can reach any significant POC) it often crumbles with noncohesive and tightly coupled, high latency and low performance. But.. its a place to start.
Try not to use shared entities between services, but if you have to first create a library with shared entities, shared logic, and code that will be used across multiple services. Get (for once :) ) inspired by those Singleton classes you used to have. Then segregate the authentication part and try adapting the monolith within the microservices architecture. If you find anything common then add it to the shared library.
If you have a sufficient team size, create your own parent, comprised of all essentials. Use TTD for each service, create one service at the time, and wait for it to stabilize before moving on. Don't chop the tree by its root, do it branch by branch. And when chopping branch by branch, do not use the same code on multiple services, otherwise, it will be again a distributed monolith.