Transformation Journey Monolith To Microservices Architecture Style - Strategic Approach.
Oluwaseyi Otun
Lead Backend Software Engineer ( Scala, Python, Java, Kafka, ZIO, Akka)
Monolithic systems are becoming too large to deal with, many enterprises are drawn to breaking them down into the microservices architectural style. It is a worthwhile journey, but it an epic journey. Microservices are an architectural style focused on the speed of software development defined as the number of functionalities created within a time unit or as the duration of the whole delivery process – from concept to deployment (time to market). The current high changeability of business environments fosters increasing popularity of the microservice approach, which forces companies to react quickly in order to avoid the situation when a good solution, but implemented too late, becomes a bad solution.
A monolithic system is very difficult to change and complex to manage. It is a delicate balancing act, one that rests on understanding how any change will affect the entire system. Onboarding new developers to the team are very difficult, as they will spend months learning the system’s codebase before they can even begin to work on it or be productive. Even the most knowledgeable of development teams are fearful of making changes or adding new code that would disrupt operation in some unforeseen way. In a monolithic system when things go wrong, operations blame development and development blames QA. Project managers blame the budget and everyone else. The business loses confidence in the IT and Development team and begins to look for outsourcers to replace the internal team.
In today’s requirements, modern web and cloud-native applications demand high availability, scalability and an agile approach for which a microservice architecture has shown to be a promising solution. However, building a microservice architecture from scratch is sometimes not possible when monolithic applications are already in place and cannot be replaced by simply flipping the switch. Instead, it is required to break up this monolith step-by-step into microservices that represent self-contained components, have their own data.
Transforming monolith application to a microservice is a bumpy road, full of technical setbacks, unexpected dependencies, and organizational challenges. It is paramount to have a clear vision of the desired situation before we diving into the step-by-step approach we need to understand the two ecosystems.
Monolith System.
?A software system is called "monolithic" if it has a monolithic architecture, in which functionally distinguishable aspects (for example data input and output, data processing, error handling, and the user interface) are all interwoven, rather than containing architecturally separate components --wikipedia
In a monolith, the codebase is partition around the module. In some monolith, the codebase is still well structured (classes and packages that are coherent and decoupled at a source level rather than a big-ball-of-mud) but it is not split into separate modules for compilation. All the modules are shipped/deployed at the same time. i.e once the compiled code is ready for release a single version is shipped to all nodes. All running components have the same version of the software running at any point in time. This is independent of whether the module structure is a monolith. Each release version for the system is deployed everywhere at once (often by stopping the entire system, rolling out the software and then restarting). All the modules share the same database.
Microservice System.
Microservices are a software development technique a variant of the service-oriented architecture (SOA) structural style that arranges an application as a collection of loosely coupled services. In a microservices architecture, services are fine-grained and the protocols are lightweight. --wikipedia
Microservices are an approach to application development in which a large application is built as a suite of modular services (i.e. loosely coupled modules/components). Each module supports a specific business goal and uses a simple, well-defined interface to communicate with other sets of services. Instead of sharing a single database as in Monolithic application, each microservice has its own database. Having a database per service is essential if you want to benefit from microservices because it ensures loose coupling. Each of the services has its own database. Moreover, a service can use a type of database that is best suited to its needs e.g Catalog Service can use the NoSQL database eventual consistency, Payment Service can use RDMB for strong data consistency vis-verse.
It is important to know that there is a high overall cost associated with decomposing a monolith system to microservices and it may take many iterations. It is necessary for developers, architects to closely evaluate whether the decomposition of an existing monolith is the right path and whether the microservices itself is the right destination. The cost and operational disruptions are very critical to key business stakeholders and top-level management. We need to justify the business value and convince them that the right path to follow, top management level buy-in is crucial and success factor in this journey.
Having cleared that out, let’s go through guide and step-by-step.
DevOps And Operational Capability: Decomposing a monolith system to microservice requires a minimal level of operational readiness. It requires continuing delivery pipelines to independently build test and deploy executable services and the ability to secure, debug, canary rollout, failing forward and monitor a distributed architecture. Operational readiness maturity is required either we are building greenfield services and decomposing an existing system. The technology to operate a microservice architecture has evolved rapidly this includes a dedicated infrastructure to build, run and reliable secure, distributed microservices, container orchestration systems Kubernetes, Docker containers, Mesos, OpenShift, Service Mesh, Service discovery Consul, distributed tracing, API management system and versioning Swagger. You will need a dedicated DevOps team that will manage and operate all these infrastructure services. DevOps enables faster delivery of quality software while bridging the gap between the developers and the operations teams.
Understanding Dependency: A major benefit of microservices is to have a fast and independent release cycle. The main motivation for moving away from the monolith is the high cost and slow pace of change of the capabilities locked in it, so we want to progressively move in a direction that decouples these core capabilities by removing dependencies to the monolith. We want to remove dependencies back into monoliths. Let consider an online store. We have our 2 core capabilities namely Order and Payment. When a customer checkout we calculate the total Order and capture the payment method and forward it to the Payment Processing module. If we need to decide which of these two capabilities to decouple next. I will suggest starting decouple the Payment first then Order. In this order Order module locked in monolith with dependency out to Payment Service microservice.
Decouple Capability and Not Existing CodeBase: As we are moving from monolith to microservices, most developers want to extract a service out of the existing code base or system. There are two ways to go about it. We either extract and refactor or a complete rewrite. Often by default, the service extraction or monolith decomposition is imagined as a case of reusing the existing implementation as-is and extracting it into a separate service. we are biased towards the code we design and write. The labor of building, no matter how painful the process of imperfect the result, make us grow a love for it. Unfortunately, this bias is going to hold the monolith decomposition effort back. It causes the developers and more importantly technical managers to disregard the high cost and low value of extracting and reusing the code. The rewrite gives us an opportunity to revisit the business capability, initiate a conversation with the business and product owner to simplify the legacy workflow and challenge the old assumption and constraints built over time into the system. It also provides an opportunity for a technology refresh, implementing the new service with a programming language and technology stack that is most suitable for that particular service.
Let consider an online store system, Payment Service critical to the business. This is service is used to process payment. The business will want to see the low cost of processing fees and increase revenue. The current business use cases give us the opportunity to route to multiple acquirers. We can now build intelligent routing capability into our Payment Service such that we can route the payment to acquiring banks with low fees to optimize our payment processing cost.
We are better off to rewrite the capability as a new service and retire the old code. It is very likely that the existing capabilities are not built around clear domain driven design concepts. This results in transporting or storing data structures that are not reflecting the new domain models. A long-lived legacy code that has gone through many iterations of change could have a high code toxicity level and lower value for reuse.
Adopt Caway's law: "Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure" Melvin Conway. When thinking over Conway’s law it is clear why are we facing the rise of microservices solutions today The good thing is that Conway’s law is applicable to any system architecture. Today, monolithic architecture violates this law when implemented in most organizations The options are as simple as either replicating organizational communication structure in the system architecture or changing the organizational communication structure, so it matches the microservices architecture. In order to do it right, you need to stay agile, most often several iterations are needed before it becomes perfectly aligned.
Benefits of Microservice Architectural Style
- Horizontal Scaling Made Easy: Microservices can be easily scaled horizontally because they are (generally) stateless there is no state (cache, session) bound to an individual instance. Adding capacity to the pool is usually as simple as adding a new microservice instance behind your Load Balancer or queue. Using orchestration technology like Kubernetes make the scaling more flexible in the case that there is a surge in requests, the application can be easily automated to scale up. If the application is not utilized enough, it can scale down (and therefore reduce operating costs if you are running in the cloud or on-premise)
- Independent Release Cycle and Bussiness Agility: Microservices have an independent lifecycle. This means that each and every service is improved upon and released (in a backward-compatible manner) without extensive communication between teams. Fortunately, due to the nature of microservices, there is a guarantee that the majority of changes will not negatively affect other components across the system. This is possible because microservices always communicate through an API and if the existing API does not change, the change is compatible with all other existing clients. On the business side from a time-to-market standpoint, this implies reduced mean time to delivery, because multiple unrelated changes in multiple modules do not need to wait for one big (and risky) deployment, each change can be deployed individually as part of the continuous delivery process. Utilization of microservices also reduces the meantime to recovery, as (if there is a bug in the solution) the team is able to deploy a new version swiftly, with only testing of the affected component being required. Business and product owners can request changes. The development team will implement and deploy to production faster in an agile manner.
- Team Motivation, Scaling, And Velocity Horizontal scaling of components is the scaling of the organization itself. That means an increase in the number of teams that can work independently. An increase in the number of teams working on a component cannot be done easily with monolithic architecture as its quite hard for new programmers to be brought up to speed, because they must grapple with complex modules, tangled by years of development and legacy code. Even most experience developer finds it very hard to be productive in Monolith system. Microservices, on the other hand, are designed to be rather small (for one dedicated team) and targeted at one business concept (such as user management). With this type of product, it is quite easy to onboard a new member, as he needs to gain experience only in a limited subdomain of the service itself. The new team member is able to get acquainted with the complexities of the system gradually. There is no longer the obligation to use the same technology throughout the entire system, as the modules are isolated by APIs. Technically speaking the system may be written using a variety of frameworks or languages (which may ease the spinning up new teams). From the recruitment standpoint, you are not only limited to hiring e.g Java developers but you have an available pool of talent from all programming languages like Scala, Golang, C++, C#, Node.js, etc. You use the right tool for the job. Most developers this day have skills in at least 2 programming languages. They will be motivated if they can put them in use and become productive.
- Organized Around Business Capabilities: Microservice architectures align teams to focus on building business functionality instead of writing glue code. In other words, development teams are organized around business capabilities and not technologies. This means that services are adaptable for use in multiple contexts. The same service can be reused in more than one business process or over different business channels depending on the need. Each team member is responsible for a particular service which results in building a smart, cross-functional team.
- Microservices Architecture Complements Cloud Computing Microservices approach complements the cloud very well. Cloud computing has matured over the years to provide highly efficient infrastructure solutions for prototyping rapidly, supporting enormous data processing and production needs, and enabling higher service levels as compared to internal IT organizations capability can provide. Serverless is a new style of cloud-computing, that focuses the developer’s attention on their business functionality and away from infrastructure concerns. The infrastructure required to execute the function is managed entirely by the cloud provider and you only pay for the number of executions of the functions and the computing power it consumes. We can implement cloud-native microservice around AWS Lambda, Azure Function, Google cloud function, etc.
Take Away
?Microservices offer a unique kind of modularization; they make big solutions easier, increase productivity, offer flexibility in choosing technologies and are great for distributed teams. It promises agility and faster time-to-market. The idea of vanishing a legacy monolith into thin air by decoupling it into beautifully designed microservices is somewhat of a myth and arguably undesirable. We must carefully plan our strategy and execute our migration in such a way that adds business value and not disrupt the existing system. Every increment must leave us in a better place in terms of the architecture goal.
If you have a large project, need quick and autonomous delivery, plan to scale your solution or need to frequently update separate parts of your system, microservices are your best bet. Netflix, eBay, Amazon, Twitter, PayPal, many other large-scale Saas, and applications have all evolved from monolithic to microservices architecture.
As we are adjusting to the new normal of Covid-19 lockdown. Please stay safe, stay indoors and practice excellent hygiene.
Thank you for reading.
Oluwaseyi Otun is a Backend Software Engineer and Data Engineer (Java, Golang, Scala, Akka). Big Data and Distributed system enthusiast with a special interest software architectural design pattern, microservice, Big Data, large scale distributed system in in-memory, streaming computing and big data analytics. I love learning internals working on systems and exploring what happens under the covers. He lives in one of the Atlantic Province in Canada.