Moving to Microservices...
Microservices architecture
Microservices concept is almost similar to “Divide and conquer” type architectural approach. This approach has been around, Service-Oriented architecture. Technologists are attracted towards microservices mainly for the following set of predominant benefits:
- Development dependencies: Minimized Code conflicts, Ability to deploy partial implementation - through isolated developments & deployments specific to each services.
- Scalability: Distributed horizontal scaling, to overcome fault tolerance issues, performance issues with geo-spatial users.
Microservices benefits/pitfalls
Microservice as a concept is an illusorily simple one. Ideally they are multiple self-contained single-service applications (extracted from centralized monolith architecture, in case of migrated) that are de-centralized/distributed and talks to each other through predefined APIs.
“If you can't build a monolith, what makes you think microservices are the answer? -Simon Brown”
Achieving these aforementioned microservices benefits are not that easy to deliver, if there are no proper architectural plans and goals predefined in the course of this movement. Microservices architectures can be become complex to manage. Distributed systems have ways of failing that you’ve never thought of before.
Let's explore further on these benefits and how to achieve a closer effect without moving away from monolith:
Development dependencies
A very common attraction to migrate to microservices architecture approach is to make it easy for managing development dependencies that are being faced by teams. If you have a large team all working on the same big codebase then that can cause clashes and merge conflicts and there will be a lot of code for everyone to understand.
Points to be considered:
Code conflicts = Mostly arose from communication/people-practice issues
So it would immediately seem very easy if all the services was smaller and isolated by a clear interface. That way each microservice could be owned by a small team who could all work together.
But the actual problems with code conflicts and clashes are mostly related to communication problems or team management problems. Although technology approach/tools will help to improvise the situation but they cannot eradicate this completely. Revisiting this in-terms of resolving people and communication issues instead will definitely improve the cultural situations.
“(With distributed team) Face-to-face communications are rare. Hence fewer connections between modules are established. The architecture that evolves is more modular as a result of the limitations on communications between developers –Harvard Study”
Local development environment overheads
In-addition, many technologist when deciding to move towards microservices, are missing to consider the local development environment related complexities that will come along with it. In case of monolith approach, this will be a piece-of-cake. Whereas it will be complex to achieve, if the microservices have multiple services inter-dependency to each other, in-terms of achieving a single business workflow life-cycle. Containerized solutions also will not be that easy when it comes to local environment setup.
Scaling-out
The self-contained nature of microservices allows an individual instance of a microservice to get de-coupled not only from other microservices but also from itself. This allows to run duplicate services in parallel. This effectively brings in horizontal scaling. As an additional effect, it provides resilience and way to achieve fault tolerant system.
Points to be considered:
Data tier isolation
Most technologists who adopted microservices still use same data tier for all of their microservices. (i.e. not isolating the data tier for each microservices to handle its own responsible data). This setup brings in just the horizontal scalability only for your service and not for the entire applications. There are challenges to achieve clean data-tier isolations in-aligning with the microservices/modular separations. One of the example is achieving distributed database transactions. This will require re-architecting the business logics adhering to saga pattern or using Java DSL with Spring Integration techniques.
“In general, application developers simply do not implement large scalable applications assuming distributed transactions. –Pat Helland”
Resilience, Fault tolerance and High availability through Scalability
If the aim for scaling out is just to achieve fault tolerance and high availability, then the easiest way to scale a monolith is to simply deploy additional copies of the subject monolith applications behind a load balancer. This is a straight-forward way to scale up if your system receives more traffic, and it involves minimal additional complexity from an operations perspective. Even on cloud environment this shall still be achieved using similar to Beanstalk techniques. Just having optimized CI/CD setting will be enough to manage these multiple copy deployments properly.
Network latencies
The multiple modules within the monolith application (with well-defined APIs) nearly have zero overhead when interacting with those APIs. This is definitely not the case with microservices, since they are often running on different host machines and require a network between the services. This can slow down your whole system considerably (if they are getting hosted on premise microservices platform). This situation becomes even worse if you have some services which need to contact multiple other services, synchronously, in order to complete a request. (i.e. response time for a particular business transaction may increase due to underlying network issues, if not established and monitored properly)
Monolith improvements, to start with
Following rough guideline stages shall be incorporated with the context of the subject monolith application if applicable. These stages will typically solve many of the problems that cause teams to move towards microservices, and without all the associated complexity/learning curve/high amount of time & effort. And yet taking it to closer to the similar benefits that we may expect from microservices.
- Refactor application to make sure to bring in automatic testing.
- Refactor application into isolated modules with clear API definitions without encouraging any overlap of code pieces into the modules directly. All modules must talk through APIs only.
- Choose critical module(s) and take it out of monolith application pack, split it into its own application on the same host (if capacity allows). The communication and security setting will have to be sorted out treating them as two different applications interfacing with each other through properly defined APIs. This will be a step that makes a module ready for further distributions in future if traffic for that particular increases
- Move separated module(s) and put it on a different host system(s) (VM(s)) for the purpose of size optimization specific to the nature of these modules. (i.e. one module may require more memory allocation than others, considering the amount of data it might require in the course of processing a request). This shall be duplicated with front-ending load balancer for achieving fault tolerance and high availability.
- Finally refactoring the data tier for bringing in the same level of isolation that has been introduced at application modules’. If it is RDBMS, try to see for options to improvise with ORM and ORM caching techniques to improve performance. It might be started with separate set of database tables isolated from each modules within the same database as an intermediate step to assess and understand the complexity involved before isolating them into separate database.
Conclusion
This is simply a summary of my observations and collation of other's experiences in the journey of migrating to microservices from monolith architecture approach. Hope technologists shall find these high-level concepts useful to factor-in while preparing/considering for movement towards microservices approach. If there are strong prerequisites (such as Rapid provisioning, Basic monitoring, Rapid deployment) that are in need of microservices architecture approach, they may exempt from these stages.