Understanding The Microservices Architecture in Simple Examples

Understanding The Microservices Architecture in Simple Examples

Definition

A microservice is the smallest unit of functionality in a system that can be scaled and deployed independently.

This software architecture allows teams to develop a system by creating small units of services that run on their own and communicate with each other through a lightweight mechanism such as an API.

Microservices vs Monolithic

To gain a better understanding of the microservices architecture, let’s compare it with the classical monolithic approach.

Wordpress monolithic design
Figure 1: WordPress monolithic design

I’m using WordPress to write this blog post, and it’s a fantastic tool for the job. However, it’s a monolithic system that can reach its limits when the business requirements exceed the capabilities of WordPress.

WordPress is a single box of tools that encompasses everything from the database to the client-side interface and the server-side application, all tightly coupled to one another. Consequently, any change to the application necessitates deploying the entire system. Additionally, if a failure occurs in the database, it can affect both the client-side and server-side components, making the system prone to errors.

Due to these limitations of the monolithic approach, engineering teams have adopted the microservices architecture.

microservices design
Figure 2: Microservices design

Benefits of the Microservices architecture

Scalability

Since the functionalities of the system are deployed independently with their allocated resources, they can be scaled individually (horizontal scalability) on demand.

Fault tolerance

Returning to our WordPress example, in a microservices architecture, a failure in the database will not result in a complete system shutdown. Instead, only the specific part that is experiencing the failure will be affected. For instance, if there is an error in the server-side functionality, it will not impact the client-side application. The client-side application may display outdated results, but it will still be able to deliver results to the user.

Observability

By employing the microservices architecture in conjunction with event sourcing, it becomes possible to achieve auditability of the system. This is often a crucial requirement, especially in financial services. In this setup, all the events within the system flow through a central event bus, enabling comprehensive tracking and monitoring.

Decisions to make when designing a Microservices architecture

No shared databases

Microservices should have their own databases and avoid using each other’s databases. This approach promotes loose coupling and prevents failures in other microservices when changes are made to a shared database.

However, in situations where microservices need to share a database for important information, it is recommended to store the information in a single database and rely on database transactions to maintain data consistency.

Handling security

The traditional approach for authenticating a microservice involved verifying the request against a database or an identity server.

However, a more effective approach is to utilize a token-based authentication method. Since microservices can encompass various functions such as accessing a database, serving as middleware against the data store, or providing a user interface, authenticating microservices within a distributed system becomes a critical aspect of ensuring the security of the microservices design.

There are several common approaches to secure communications between microservices. One approach is Single Sign-On (SSO), which allows users or identities to access multiple services seamlessly. Another approach is using JSON Web Tokens (JWT), which enables the secure sharing of multiple properties between a client and a microservice. For further details on these approaches, I recommend reading the referenced article.

Handling microservices flow

In order to ensure that microservices can provide the required results to the user, it is essential to organize their workflow effectively. Two commonly used approaches for achieving this are:

  1. Drive flow from client: In this approach, the client application takes charge of coordinating the workflow among the microservices. The client sends requests to the appropriate microservices and handles the orchestration of the overall process. This approach provides more control to the client but can result in increased complexity within the client application.
  2. Choreography: In the choreography approach, each microservice is responsible for its own behavior and communicates with other microservices through events. Each microservice reacts to the events it receives and performs its designated tasks accordingly. This approach promotes loose coupling and decentralized decision-making among microservices but can require careful coordination to ensure the desired overall behavior.

The choice between these two approaches depends on the specific requirements and complexity of the system.

Avoid microservices dependencies

It is crucial to avoid designing microservices with tightly coupled dependencies, as this can lead to a monolithic architecture rather than achieving the desired microservices architecture. The concept of “dependency hell” highlights the challenges that arise when dependencies between microservices become complex and unmanageable.

To understand and learn more about the issues associated with dependency hell, I recommend reading the post provided in this link. It will provide valuable insights into the potential pitfalls and strategies for avoiding such problems in microservices design.