In this blog, I'm going to talk about microservices. Here a short overview of what you will learn:
What is a Monolith?
Challenges and why Microservices?
What are Microservices?
Benefits and Best Practices
Challenges of Microservices
Communication between Microservices
First, I will start by explaining what a monolith application architecture is, what were some of the challenges of a monolith architecture, and why the industry moved slowly toward a microservices architecture.
Then, we will precisely see what microservices or microservice architecture is, as well as best practices, benefits, and how the communication between microservices works. We will also see different ways to manage code for microservices applications and discuss mono-repo versus poly-repo and both advantages and disadvantages.
So let's get started. 🙌
This is the written version of our popular Youtube video. Check it out to have helpful animations.
Monolith and its Challenges
Before microservices, the standard way of developing applications was with a monolithic architecture. It means all the application components, the whole code, is part of a single unit.
For example, if we had an online shop application, all of its parts like user authentication, shopping cart, product catalog, sales campaigns, notification, and so on, all the code for these functionalities would be in one code base as part of one monolithic application:
Everything is developed, deployed, and scaled as one unit. This means the application must be written in a single language with one technology stack with a single runtime. And if you have different teams working on various application parts, they must coordinate to ensure they don't affect each other's work. Also, if developers change the code for the payment functionality, you would need to build the whole application and deploy it as one package. You can't just update and deploy only the payment functionality changes separately.
Challenges ⚡️
This was a standard way of developing applications, but as applications grew in size and complexity, this led to different challenges.
First, the coordination between teams became more difficult, because the code was much bigger, and the application parts were more tangled. Also, if suddenly you had a usage spike in shopping carts, for example, on holiday dates, and you wanted to scale only that part of the application, you can't do it. You need to scale the whole application. This, in turn, means higher infrastructure costs and less flexibility in scaling your application up and down.
Another issue is, for example, if a payment functionality used a third-party module with version 1.8, while notifications feature needed the same module, but required version 1.7 instead. In a monolith application, you would have to pick one or the other, because it's a single application, and you can only have one dependency of the same module.
Another major issue with monolith applications is that the release process of such applications takes longer, because for changes in any part of the application in any feature, you need to test and build the whole application to deploy those changes.
What are Microservices?
The answer to all these issues was a microservices architecture. So what are microservices exactly? 🤔 With microservices, we break down the application into multiple smaller applications. So we have several small or micro applications that make up that one big application.
Important Questions to answer when building a Microservices Architecture
When we create a microservices architecture, we have a couple of fundamental questions. First, how do we decide how to break down the application, what code goes where, and how many such micro applications or microservices do we create? How big or small should these microservices be? And finally, how do these services then talk to each other?
Best Practices 1: Break down based on business functionalities
First, the best practice is to break down the application into components or microservices based on business functionalities, not technical ones. So the microservices of an online shop application will be products, shopping cart, user accounts, checkout, and so on, because all these are business features.
Best Practices 2: 1 service for 1 specific job
In terms of size, each microservice must do just one isolated thing. So, it would help if you had a microservice responsible for shopping cart logic and another for checkout. It would help if you always strived to keep one service doing one specific job.
Best Practices 3: Self-contained and independent
An essential characteristic of each microservice is that they should be self-contained and independent. This means each service must be able to be developed, deployed, and scaled separately without any tight dependencies on any other services, even though they are part of the same application. This is called "loose coupling".
With this best practice approach, if you change something in the payment service, you will only build and deploy the payment service. Nothing else will be affected, which means the services have versions that are not dependent on others. So, if I release one service, I don't need to release any other service. So this release cycle has to be completely independent.
How Microservices Communicate With Each Other?
Now, if these services are isolated and self-contained, how do they connect? Because obviously, the payment service will need something from the user account to process the payment, or the checkout service will need something from the shopping cart, right?
3 Ways of Microservices Communication
1. API calls
A widespread way for microservice communication is using API calls. So, each service has an endpoint on which it accepts requests from other services. Services can talk to each other by sending HTTP requests on these endpoints. This is a synchronous communication where one service sends a request to another service and waits for the response. So, the user account service can send an HTTP request to the payment service on its API endpoint and vice versa.
2. Message Broker
Another common way of communication between microservices is using a message broker with asynchronous communication. Here, services will send messages first to the intermediary message service or a broker such as RabbitMQ for example. Then the message broker will forward that message to the respective service. So again, the user account will send the message to the broker saying, "Please pass this message on to the payment service," and the message broker will then forward that message to the payment service.
3. Service Mesh
A third way of communication between microservices, which is becoming popular, especially in the field of Kubernetes, is using a service mesh. With service mesh, a helper service takes over the complete communication logic. So, you don't have to code this logic into the microservices and have this communication logic delegated to this external service.
To learn about Service Mesh, you can check out my Youtube video on that: Istio and Service Mesh - simple explained in 15 mins
These are different communication options, and since the services are all isolated and talk to each other either with API calls or using additional services, you can even develop each service with a different programming language. You can have dedicated teams for each service that can choose their own technology stack and work on their service without affecting or being affected by other service teams. And this is exactly the most important advantage of microservices architecture compared to the monolith.
Downsides of Microservices ❌
However, these benefits come with a price. While microservices make developing and deploying applications more straightforward in many aspects, they also introduce some challenges that were not present.
When breaking down an application into multiple pieces introduces a lot of complexities and challenges. One of the main complexities is configuring the communication part between the services. A microservice may be down or unhealthy and not responding yet, while another service sends requests to its API, expecting a fulfilled response, which can result in unexpected results.
Additionally, with microservices deployed and scaled separately, it may become difficult to keep an overview and identify when a microservice is down or which service is actually down when something in the application is not working properly. Therefore, a proper configuration of the application setup and its components is necessary to ensure that the application functions well.
Fortunately, there are various tools available to make running microservices applications easier, despite their complexity:
The most popular one is Kubernetes, which is a perfect platform for running large microservices applications:
CI/CD Pipeline for Microservices
An essential element of deploying microservices is a CI/CD pipeline. Many companies with microservices applications deploy multiple times a day, such as Amazon, Google, and Netflix. Those companies have applications with hundreds of microservices that they deploy thousands of times per day. Therefore, in the modern workplace, you will work with microservices and need to know how to configure the release process with a CI/CD pipeline for microservices.
Monorepo vs Poly Repo - How to Manage the Dose for Microservices Application?
We said microservices are when application components are developed and deployed separately as individual micro-applications. So the question is, how do we manage the code for microservices applications in a Git repository, like GitLab, for example? 🤔
With one project, it's simple:
We just have one application and it gets its own Git repository. With microservices applications, we have two options for how the code is managed: monorepo, which stands for a single repository, and poly-repo, also known as "multi-repository".
Monorepo Explained - Benefits and Disadvantages
Monorepo or single repository, means having one GitLab repository for all the services. So, we would create one project for a mono repo. What's the difference here, or how do we structure multiple micro applications inside one application repository? Well, a common way is using folders:
You have folders for each service, like shopping cart, payment, notifications, etc.; all the codes for those services are in their respective folders.
Benefits of Monorepo
✅Having a mono repo, meaning all the services are still in one repository, makes code management and development easier, because you only have to clone and work with one repository.
✅ Plus, if you have some shared code between the services, like Kubernetes manifest templates, Helm charts, or Docker Compose, you can put them in the project's root, and all the services can reuse them.
Challenges of Monorepo
But mono repo also comes with some challenges.
❌ As I mentioned, the essential criterion of microservices is to be completely independent and isolated, so there should be no tight coupling between the services inside the code. It becomes easy to break this criterion when you have a mono repo. So, if you have junior developers with less experience in the mono repo setup, it's easier to make such mistakes and develop tightly coupled logic or code in your services.
❌ Another downside of the mono repo is that when the application becomes really big, cloning, fetching, and pushing become slow because your project is vast.
❌ Regarding the CI/CD pipeline, you can only create one pipeline for one project in most CI/CD platforms like GitLab CI/CD or Jenkins.
So, you are building multiple services with a single project pipeline, and that means you need to add additional logic in your pipeline code that makes sure only to develop and deploy the service that has changed. If you make code changes in the payment service, your pipeline code should detect that, and only that service should be built, tested, and deployed. It is possible to do that, but more challenging.
❌ One more issue with a mono repo is that since you have just one main branch, because you have one repository, if developers of one of the services break the main branch, other services and their pipelines will be blocked as well.
But still there are a lot of companies, including huge ones like Google, who actually use mono repo for their applications.
Polyrepo explained - Benefits and Disadvantages
The second option, which is probably a bit more preferred, is poly-repo or multiple repositories. With this approach, for each service, we create a separate git project, so the code is completely isolated:
Benefits of poly-repo
✅ You can clone and work on them separately, because they are in separate repositories. Now, even though they are separate application repositories, they are still part of this bigger application. So, of course, you would want to still have some kind of connection between these reports for easy management and overview.
Suppose you're hosting your code repositories on GitLab, for example. In that case, you can use GitLab's feature of groups to group code for all the microservices that belong to the same application in one group to make managing those repositories easier. Essentially, you would create a GitLab repository group for your application called "my-online-shop":
Inside this group, you can create a separate project for each microservice that belongs to that application. If your company has multiple microservices applications, this will help keep an overview of what projects belong together. But also, within the group, you can create secrets or other CI variables that all the projects can share in that group.
✅ Now, what about the CI/CD pipeline for a poly repo? For poly repo, the CI/CD configuration is more straightforward because you have one pipeline for each repository. So, no extra logic is needed to differentiate between the services.
Downsides of poly-repo
Of course, everything has advantages and disadvantages. So, for poly repo as well, you have some downsides.
❌Having application code in multiple repositories can make working on the project harder, especially if you need to change two or more services simultaneously, because a feature or bug fix affects various services. If you need to switch between the services often, this can also be tedious.
❌ Plus, searching for something across multiple projects from the code editor can take time and effort.
❌Also, in the poly repo, you can't share files in the project like Kubernetes or Helm manifest, Docker Compose, etc. You would have to duplicate them in each project's repository or create a dedicated project and reference them from there.
Which one to choose when? It depends
As you see, both options have their advantages and disadvantages. Still, the general rule is that if you have a small project with just several microservices, you should stick to the mono repo and save the overhead of creating, managing, and checking out multiple repositories. On the other hand, if you have separate teams for each service, if you want complete isolation, a smaller code base to clone on pipelines, and so on, then the poly repo would be a better option.
Now, I hope this gave you a great introduction to microservices and now you understand what it is and why everyone is using it. 😊
GitLab CI/CD course 🚀
If you want to know how to build CI/CD pipelines for microservices applications, you can check out my complete GitLab CI/CD course. In this course, I show hands-on demos of how to build CI/CD pipelines for microservice applications in a mono repo and poly-repo. I also show how to deploy a microservice application to a Kubernetes cluster.
You can also see a detailed overview of the curriculum here:
Like, share and follow me 😍 for more content: