This is a long boring introduction to Microservices Architecture, I will try to make it as relevant and interesting as possible. You will learn the differences between Monolithic and Microservices architecture. The various strategies used to decompose a Monolithic system into Microservices based architecture. It is difficult to give you an overview of this architecture in a single article, but I will try my best to do this. Before getting into the details of this new normal, let us spend some time recapping the Monolithic architecture.
1. A sweet little Watch Store
A small businessman approached you for a Software System for his Watch Store. You developed a system that could manage the inventory, take care of the Point of Sale, deal with customer relationships, and so on. You built a Monolithic system just to fulfill the small business guy’s need and handed it over. He received the system happily.
But, after a few good months, the same guy meets you again and shares his thoughts to scale up his business. This time he wants to go online. He wants to have a website to sell the watches online. The same customer-facing app should be available as an Android app and WebApp. You suddenly realized that you need to build everything from scratch for this new requirement (Android app). Cause the earlier system you built is a tightly coupled system, you will not be able to reuse anything from that.
Just take a moment and think. The customer’s requirements are like a never-ending Reactive stream, new requirement keeps coming. You need to design a system from day one that is flexible enough to adopt new changes, otherwise, you will not be to keep your customers happy. We are talking about microservices in this article which allows you to think in a certain prospect when you are architecting a software system. There are several other advantages of using the new architecture over the traditional monolithic one, we will discuss them in detail below.
2. Recapping Monolithic Architecture
If you have landed on this tutorial from a Search Engine, I am sure you have some understanding of Monolithic Architecture. Let us try to understand the Pros and Cons of Monolithic architecture.
The Watch store app you built earlier would look similar to the above. All the different services are tightly coupled and packaged as a single
WatchStore.war file. The war file is then deployed in a Java Servlet container (like Tomcat or WAS Server) and an HttpServer (like Nginx or Apache server) is used as the reverse proxy to serve the web. As you can see, the entire application is deployed in a single server. When you want to update a specific service, you would need to redeploy the entire war. If any service crashes, the entire application goes down.
2.1. Pros of Monolithic architecture
- Simple to develop – Common development tools and IDEs are enough for code development for monolithic applications.
- Easy to deploy – Just need to deploy the WAR file (or directory hierarchy) on the appropriate runtime.
- Simple to scale – You can run multiple copies of the application behind a load balancer (Nginx).
- Debugging and Tracing are simple.
2.2. Cons of a Monolithic application
Monolithic best fits for a small scale application, problem arises as the application starts scaling up (features keep growing).
- A Stiff learning curve for new developers to go through the entire code base.
- The large monolithic application brings down productivity. Because a lot of time is wasted by waiting for restarting that application and testing during the development.
- A small mistake can break the entire application.
- Continuous deployment is inefficient. Large application takes a lot of time to build, deploy, and restart the server to containers. This induces a lot of downtimes.
- Scaling the application is challenging. You can only scale it in one dimension.
- Less support for Polyglot technology. The technology stack is very tightly coupled. If you want to mix Java and Nodejs you will face challenges.
- Centralized Database. Usually, a single database is used for all the services, which can lead to a single point of failure.
- Hardware limitation can occur: If you are building a really large-scale application as a monolithic system, often you will find the hardware limitation changes.
3. What is Microservices architecture?
It is a software architectural approach in which a single application is developed as a suite of independent services, each running services are independently deployable and they often communicate with each other with HTTP Resource Apis.
- Each service in a Microservices architecture can be written in different programming languages.
- The services are often monitored with a Centralised monitoring system.
- Each service often has a separate persistent system that allows it to be deployed independently.
- Each service is tightly coupled around its business logic, which allows it to run independently.
- In an advanced microservices system, an event bus (JMS, RabbitMQ, or Apache Kafka) is used for inter-service communications.
- Typically, all the microservices run behind an API Gateway which acts as a Proxy for the outside world (public traffic).
The above diagram represents a high-level microservices architecture of the Watch Store. We will look into a more technically appropriate design later. Here you can see, that both the Web-UI and Android App communicates with the application through an API gateway. The Client Apps need not know where exactly each of these services is physically deployed. The API gateway typically works as a proxy and talks to the individual services using REST-like lightweight protocol.
NOTE: We often talk about a polyglot persistent system in microservices. This basically means each Microservice should have its own database and the service itself can be written in any programming language as per the technical feasibilities. Each of these services also can invoke other services via REST or a Message Broker.
4. Pros and Cons of Microservices
Microservices architecture is undoubtedly the new normal of enterprise application design. It brings flexibility and resilience to your software systems. But you should also remember that every software design has strengths and weaknesses that we must evaluate before deciding whether to use the Microservices pattern or Monolithic pattern.
Pros of the Microservices
- Strong Module boundaries
- Better Continuous delivery and deployment
- Scope for Polyglot Technology
- Built for scale and complexity
- Improved Fault tolerance
Cons of the Microservices
- Complexity due to distributed nature
- Software Design Complexity
- Development complexity
- Deployment complexity
- Eventual consistency
4.1. Strong Module boundaries (Pros)
Each service in this architecture has stronger Module Boundaries than Monolithic. Stronger module boundaries mean each service is tightly coupled around the specific business logic. Dividing a large system into independent services provides several benefits.
If you need to make any changes to a specific feature, you only need to understand that service (module) and do the changes. Finding the code that needs to be changed is much easier in a small service than in a large complex system. Additionally, you can assign a small dedicated team for each service and they can carry out feature releases independently. As an example here, if the same Watch Store client comes to you asking for a CRM feature in the developed system, you can easily add a new independent CRM Module to the existing architecture.
4.2. Better Continuous delivery and deployment (pros)
When a large application is converted to a group of services, it is easier to build and deploy them. With the help of modern CI/CD tools like Jenkins, JFlog, GitHub and etc it is possible to have better pipelines to continuously deliver new releases. It fits into the modern-day workflows and agile delivery models.
A smaller service takes lesser time to deploy, the server can be restarted quickly. This brings down the significant waiting time before the instance is ready to serve the client requests. The core features in Microservices like the Circuit breaker and Load Balancer further make it easy to roll out a new version of the module with lesser pain.
In this architecture, you make a change in the specific module/service, test it, and then it is independently deployed without bringing down the entire system. Even if a complete failure happens due to the new changes, this small service should not bring down the entire system. In a microservices architecture, even if a particular service completely fails for whatsoever reason, the rest of the system is expected to work. For example, even if the order processing module is down, the user should be able to add the products to the shopping cart. Also, the user should be able to place an order and once the order processing module is up, it can send out the confirmations.
4.3. Scope for Polyglot Technology (pros)
As each module is an independently deployable unit, you have considerable choices to pick the right technology for the Job. For example, the module that deals with Machine Learning could be written in Python, and the module that deals with Calculations is written in Java. And these modules could communicate with each other via REST, GraphQL, or Events. Microservices also allow you to choose a polyglot persistent system. You can use MySQL for transaction management, MongoDB for document storage for specific modules, and so on.
With Monolithic, you need to take the early decisions on technology and difficult to reverse back in the future. However, in microservices, you can rebuild any module in any technology as long as it is capable of communicating with other services.
Another aspect is that you could have multiple versions of the same module. You can notice this in any public API like Github, usually, they have multiple versions of the same APIs. It is not easy to achieve this in a Monolithic system.
4.4. Built for scale and complexity (pros)
A Microservices system is designed for large complex Enterprise applications. A good example is Twitter. Twitter can scale up dynamically during high peaks and can scale down during low peaks.
Each service in this architecture can be scaled up independently unlike the Monolithic system. In other words, if the Audit Service is responding slow, you can spin up another instance of the same service to distribute the load. In the case of a Monolithic system, you will need to install the entire application in another instance to scale up.
A large complex system can be brought down to small independent services to make the system flexible to address new changes. A complex system is difficult to maintain and monitor. You can use centralized monitoring tools to constantly monitor each service.
4.5. Improved Fault tolerance (pros)
It is built with better fault tolerance. For example, a JVM memory leak in one service can cause disruption to that specific service only, it will not be affecting the rest of the system. With the help of a monitoring tool, you can easily detect the specific system and eliminate such an issue.
In the case of a Monolithic application, all services are run on a single JVM. If an OutOfMemoryError Exception occurs, all the services hosted on the same JVM will go down and hence the entire application.
4.6. Complexity due to distributed nature (Cons)
In a typical real-world application, you will have a reverse proxy (like Nginx) serving the web. The reverse proxy forwards the requests to an API Gateway (like Spring Cloud Gateway). The gateway will check the route configuration and then forward it to a downstream service (like the Order processing service). The Order processing service then may need to call several other services and database calls and then return the result in reverse flow.
WebClient ↔ Nginx ↔ Spring Cloud Gateway ↔ Order Processing Service ↔ Database
Microservices is a Distributed system where modular services are deployed independently. But this distribution nature brings in concerns like delay in the response throughput. This is what we just talked about in the first part. While designing a distributed system, you need to make sure there are minimum required communications between the services, and the services are hosted on a powerful infrastructure with less network latency (like modern cloud platforms). For more on the complexity, refer to the research paper – Fallacies of Distributed Computing Explained.
Microservices-based applications must use SAGA to maintain data consistency across the services. Otherwise, over a period of time, the data stored in each service could become obsolete.
4.7. Software Design Complexity (Cons)
- Designing an independent Module boundary is challenging. Software architects need to come up with the right design where each module is tightly coupled around its functionalities and loosely coupled with the rest of the modules. This way it will need to interact less with other services. Various techniques are used to decompose an application into microservices
- Decompose by Business Capability
- Decompose by Subdomain
- Self-contained service
- Service per team
- Deciding upon the persistent layer needs considerable thought as we talk about scale and loosely coupled systems. A common database for the entire application can cause a single point of failure, at the same time having independent databases for each service can cause an eventual inconsistency. The followings are techniques used to design the data layer.
- Database per Service
- Shared database
- API Composition
- Domain event
- Event sourcing
- There are several other complications in microservices architecture such as deciding upon the inter-service communications, service discovery, security, and resilience, distributed tracing, and log management.
4.8. Development complexity (Cons)
Most of the development tools like IDEs, Test tools are designed for Monolithic architecture. Even the best tool does not provide much support for developing distributed systems. Organizations need proper workflow, tools, and develop skills in order to make the microservices system successful.
- Developers need to implement the inter-service communication mechanism.
- A partial or complete failure of any service should not cause the entire system to crash.
- Implementing requests that span multiple services is more challenging
- Testing the interactions between services is more difficult
- Verifying the large-scale system behavior is more challenging due to its distributed nature.
4.9. Deployment complexity (Cons)
Deploying features that span multiple services requires careful coordination between the various development teams. You must have a rollout plan that orders service deployments based
on the dependencies between services. In the case of a monolithic architecture,
you can easily deploy updates to multiple components atomically. There are several deployment patterns that an architect needs to choose depending on the application scale and complexity.
- Multiple service instances per host
- Service instance per host
- Service instance per VM
- Service instance per Container
- Serverless deployment
- Service deployment platform
4.10. Eventual consistency (Cons)
In my experience, while booking a cab ride in Ola, sometimes two cabs get booked for a single ride. Also occasionally, the Android app says no cab could be found, but I get an SMS (text message) saying that a cab was booked successfully. As you can see, a distributed architecture can cause inconsistency in the system and hence the customer’s frustrations.
Inconsistencies can have a serious business impact. Business logic can end up making decisions based upon inconsistent information. You need to have the right logic for managing distributed transactions and data consistency. The developers need to be aware of consistency issues. The team should be able to figure out how to detect when things are out of sync. This is why proper Audit log and events management logic should be in place.
5. When to use Microservices?
You have seen the pros and cons of microservices architecture. Fortunately, there are enough tools and technology supports to overcome most of the cons. You really want to calculate the benefits and damages that will be caused by microservices in your existing application.
- Check if your application is large enough that needs to be broken down into microservices.
- See if dividing into independent modules makes sense.
- Understand if you need a distributed transaction management system.
- Do you need an inter-service communication mechanism?
- The cost of the distribution system should be lesser than the benefits it does to your system.
- Most importantly, check if you have the technical skills to start with microservices or if you want to start with monolithic and then in the future convert it to microservices.
Further reading – Medium.com – Stop you don’t need microservices.
Microservices architectures are no silver bullet. They have the same complexities as distributed systems. You will have to get better at planning and managing the development, testing, deployment, and monitoring. Interservice communications and distributed transactions have to be designed well, otherwise can cause a lot of pain. It is undoubtedly the new normal for application design. Give some proper thoughts before jumping into microservices.
In the upcoming articles, we will explore the various features of microservices and how to overcome the challenges due to their distributed nature.
Nice article and so straight to the point. I enjoy reading your Blog posts.