1. Introduction
Microservices are an architectural paradigm where different business components of a system are developed and packaged separately. These components are built and deployed as independent services from one other.
This contrasts with the more familiar monolithic architecture, where all components are developed as part of a single application.
In this tutorial, we’ll see how microservices work.
2. Characteristics of Microservices
Here, we intend to highlight some common patterns we see in applications developed using this architecture.
2.1. Components as Services
A component is an independent unit of software that can be developed and deployed separately. This is not to be confused with a library. Libraries form a part of larger software and can not be deployed independently:
Another way to look at it is that a library runs as part of a process, while a component runs as a different process. Libraries are accessed in memory within a program, while components can be accessed through API calls, gRPC, and message brokers.
Now that we understand what a component is – and, more importantly, what it isn’t, let’s see how microservices operate (using many services). We can define a service as a self-contained application component performing a particular function set. The microservices architecture derives components for the various independent business units. For example, payment, shopping, and shipping. This contrasts with a monolithic architecture where the design is based on development units such as frontend, middleware, and database.
2.2. Decentralized Data Management
With microservices, every application has its own model or data. This contrasts with enterprise applications, which generally have a centralized database. For example, the product and order management services in an e-commerce system can each have their database in a microservices architecture. In a monolith, there would only be one single centralized database.
2.3. Autonomous Development and Deployment
Each component follows its development pipeline and is deployed separately. This is a key feature of microservices. Let’s take an example of an e-commerce system. Independent development teams would handle each module (products, orders, payments, etc).
On the other hand, we would need to build, test, and deploy the entire application as one in a monolithic design.
3. Advantages of Using Microservices
3.1. Flexibility and Agility
We can use different programming languages to implement each component. This allows us to exploit the unique features that each language presents. For example, we can use C/C++ to develop a system-level component, Javascript for a web component, and Rust to develop a memory management service. This practice of taking advantage of the various strengths of each language in our application is known as Polyglot programming.
Another advantage we attain is that our teams get the flexibility to use the tools they are familiar with. Consequently, this results in higher productivity.
3.2. Easy Maintainability
Since components are built independently, we can change one without affecting the rest. This modular design ensures we don’t need to build and redeploy the entire application whenever we change.
3.3. Fault Tolerance
The failure of one component doesn’t necessarily affect the running of the entire application.
3.4. Easy Scalability
We must put multiple instances behind a load balancer to scale a monolithic app. As we can see, this leads to resource wastage. Conversely, we would have to allocate more resources to a particular component that needs them in a microservice design.
Furthermore, if the demand for a given service is reduced, we would reduce its allocated resources. We wouldn’t be able to apply this same logic with a monolithic app.
4. Challenges in Microservices
4.1. Code Management
The siloed nature of developing each component leaves us with the big question of what repository structure to use for version control.
We can choose from two approaches: a monorepo or a polyrepo. In a monorepo, all the components are in one shared repository, and all teams use this. Conversely, each component is in its repo in a polyrepo design:
The choice of which repository type to use isn’t straightforward. In a monorepo, developers are mostly likely to couple different components, which is self-defeating regarding this new architecture. Junior developers are especially vulnerable to this. In a polyrepo, we can face issues of dependency management. It can be not easy to coordinate versions of different libraries or components. Generally, we need a skilled systems architect to decide which tradeoffs are acceptable and, consequently, which repository structure to use.
4.2. Data Consistency
Handling transactions in a distributed architecture is a key challenge for microservices. Transactions ensure that the whole operation is carried out, or if any part fails, it rolls back the application to its previous state.
In a monolith, we handle data consistency using transactions. Regarding microservices, this approach causes a temporary coupling, which is a serious concern across many services. Therefore, system architects encourage transactionless coordination between services, with consistency only being eventual and any problems that arise being dealt with by other operations.
5. Conclusion
In this article, we discuss the microservices architecture that is normally used to modularize projects with tens or even hundreds of components. This architecture offers a more flexible and scalable approach to designing large applications.