In the world of enterprise application development, scalability, adaptability, and flexibility are not just keywords used in marketing but are fundamental characteristics of an application if it hopes to survive in our dynamic world.
One way of ensuring that your application would be able to adapt to changes is by breaking it into smaller and more easily managed parts. What you get is an architectural style that structures an application as a collection of services that are highly maintainable and testable with loosely coupled parts that can be independently deployed and organized around business capabilities.
These smaller service parts are what are known as microservices. The microservice architecture enables the rapid, frequent and reliable delivery of large, complex applications. It also enables an organization to evolve its technology stack.
Companies usually follow the premise that if it's not broke then there is no reason to fix or change it. So the consideration to migrate to a microservices based architecture is not one usually made as a result of wanting to keep up with technological innovations, especially for solutions and applications that are already deployed and running. But usually as a direct result of issues and slowdowns that appear in the pre-existing monolithic solution as a whole or in one of its corresponding parts.
This mindset should not be considered strange as it is an epic journey migrating a system over to using microservices. Even when just a small portion is being converted.
Do you need to migrate?
As stated earlier, the choice to migrate to a microservice architecture is one usually made from nessecity. Existing applications already have structures in place for its operations but when issues start to prop up or expansion comes into consideration then this migration would start to make sense.
Notwithstanding this, there are seemingly a few advantages to building monolithic applications. One of which is that you have all your coding in one place and your coding is very much centralized. When everything is running through the same app, it's easy for developers to hook up components to cross-cutting concerns, such as logging, rate limiting, and security features such as audit trails and DOS protection.
Having everything work right out of the box is always great but each future change would require a massive amount of effort that is in some instances equal to the amount used to create the application in the first place
With time old monolith systems are bound to undergo changes and this can be both good and bad. The good news is that changes allow them to adapt to current needs. But the bad news here is that there is a level of complexity involved when some or part of the system needs to change. This would involve taking apart a whole infrastructure just to fix or modify a seemingly small aspect of the whole system.
The resulting downtime isn’t a desirable effect. This is further complicated when some part of the system is in need of using technologies/programming languages that differ from the original it was designed in.
What are the fundamentals of a monolith to microservice oriented architecture migration?
While migrating to a microservice architecture might be of benefit to an application, it does not mean that the switch will be easy or simple. On many occasions, people tend to think that moving into microservices will decrease system complexity when in fact the complexity is just dislocated. What is worse, the complexity may now be moved into areas that the team is not experienced with. In a monolith application, be it a complex and badly maintained project, having a skilled team can still provide some level of efficiency. A bunch of microservices, on the other hand, can introduce edge cases and problems that would be extremely hard to track and solve by the same team.
A key point of note in the migration process is that migrating to a microservice architecture is not a magic cure-all for your development problems plus it's not a shortcut. Basic programming ethics still apply. Attempting to adopt the microservice architecture (an advanced technique) without (or not committing to) practising basic software development techniques, such as clean code, good design, and automated testing will only increase your workload and generate issues down the line.
This also has a lot to do with team readiness. Your development team must be trained in both the technology used in the migration process, have a good understanding of the microservices architecture and are equipped to handle any fallout. This applies to both an in-house dev team and one that is outsourced.
How to begin the Migration process?
It's an established fact that one should learn to walk first before you attempt to run. When decomposing a single entity into multiple parts, it might be worth finding a perfect, first cutting line.
Put simply, migration is a large undertaking and there are bound to be issued along the way. One approach to the process is to start small. Breaking down the monolith system and taking out a non-critical component. This means extracting something with a very limited feature set - let’s say: authentication. One may say, that something like that is not worth extracting, but on the other hand, we may learn a lot during the process and establish a framework for extracting bigger chunks.
A different approach might be to find natural boundaries between entities in the application or project. By entities, we are referring to modules, domains, bounded contexts, large features, namespaces, integrations. We are looking for things that use a narrow API to communicate with the rest of the system. Then we need to pick one that might benefit from scaling/sharing/maintainability and go from there.
Then think about APIs, how complex/wide those are? Is the process you want to extract handled asynchronously or not? How coupled it is on the database schema level with the rest of the system… There are many variables and it definitely needs a good amount of research and effort to make an educated guess about where to start.
There is a low hanging fruit that can be a good starting point in many cases, which is separating frontend from backend. Even though it is really remotely related to microservices, it might be a great starting point for initial cleanup and can benefit a lot in the long run if executed properly.
Once you have selected the chuck or segment that will be your springboard into the microservice migration route it is wise to take note of the challenges, issues and general process that was needed. As stated earlier, this allows you to be able to take out the next chuck/segment and avoid the previous pitfalls.
Why establish a framework for migration?
As we have repeatedly stated migration from a monolith system into a microservice architecture is not a simple or easy task. But having a framework for the migration process will allow for a seamless experience. It is vital to have consecutive releases delivered in an efficient manner, and you will need tooling that will allow you to achieve that. This framework will consist of a number of parts including a Continuous Integration / Delivery pipeline. Plus a logging solution that will allow you to track process execution using some sort of correlation tracking.
Also, the API - the well-established standard of describing and enforcing communication contracts between services - may not be critical but will make life and architecture maintenance much easier. Next would be a monitoring system. It might no longer be that obvious that something just broke or that messages are being lost. Finally a framework for integration testing. Testing is no longer as trivial as in monolithic applications and requires its own strategy and framework.
We have our framework ready, can we proceed to implementation?
While not can be tempting to get the process running as quickly as possible. It might be beneficial to think about at least one more aspect of the migration - how the services are going to communicate with each other. Probably the key question is: should the communication happens synchronously or not. Asynchronous communication is inherently more complex, especially when it comes to the infrastructure required, but has lots of advantages and makes the whole architecture much more flexible.
Still, for simple solutions synchronous, REST API based communication might be good enough. There are two keywords that might be worth looking into which are RPC and Message Brokers.
What else we should take into account?
During the process of migration, we also have to take a look at the technological choices for each microservice that is being used. Microservice based architectures often encourage teams to experiment with various technologies, frameworks, and languages. “Polyglot programming” one may say. It may have the benefit of using the right tool for the right job but is also a double-edged sword. Maintaining a new technology in a team poses a risk of losing human talent due to any reason and being left with a code that cannot be effectively maintained - this is especially true for both fresh/niche/exotic and really outdated technologies. If we decide to use language XYZ, let’s be sure this is something that really addresses some real problems we have and that we are sure we want to invest in this technology in the long term.
This being said, one more thing came to my mind. Migration from a large monolith into microservices, even if justified, is a significant endeavour and requires a lot of effort. It requires a well-established plan with milestones and goals we are after. This plan requires securing a budget and time of the well qualified, dedicated team. It might be worth considering introducing an external team for the time of such transition. Such a team can share their own experiences, processes, tooling, etc. saving lots of time and funds as a result.