Microservices Demystified: Unleashing the Power of Scalable Software Architecture

Microservices Demystified: Unleashing the Power of Scalable Software Architecture

Gather together those things that change for the same reason, and separate those things that change for different reasons -- Uncle Bob

Hey there, everybody! Have you ever wondered how some applications can scale up and down quickly and can handle millions of users without breaking a sweat? Well, the answer to that question could be -- Micro Services. Today, in this article we're diving into the world of microservices and uncovering the secrets behind their growing popularity in the realm of software development.

Architecture & Design Principles

The main motivation behind the microservices architecture is to split up a monolith application into several smaller loosely coupled services that can be developed, deployed and maintained independently. Each of these services performs a specific task and communicates with each other to solve business problems.

The Single Responsibility Principle

Microservices architecture follows the Single Responsibility Principle by splitting up a system into multiple services, it divides the responsibilities among different services. These microservices can then communicate either synchronously, through Internal API calls or asynchronously by utilising a service such as RabbitMQ as a message broker.

Decentralized Data Management

In a microservice architecture, where each microservice has its own unique data storage or database, decentralised data management is a vital component. Microservices encourage data autonomy and isolation in contrast to conventional monolithic systems, which often share a centralised database. With this method, each microservice is given the freedom to select the data storage system or database that best meets its individual needs.

These options include relational databases, NoSQL databases, or even specialised data stores like graph databases or search indexes. Independent scalability, fault isolation, and flexibility in data modelling and access patterns inside each microservice are made possible by decentralised data management.

Additionally, it lessens coupling between services because they can independently change and update their data schemas, promoting agility and streamlining continuous delivery procedures.

Communication Among MicroServices

External Agents can communicate with the complete system through an API Gateway. The API gateway receives a request, say from a browser, and communicates with the corresponding microservice that can fulfil the request. Internally, this microservice can communicate with other microservices to build up the required response and sends it back through the API Gateway.

Microservices communicate with each other either synchronously or asynchronously.

  • In synchronous communication, the caller engages in a blocking operation where it waits for a response from the callee microservice. This means that the caller keeps on waiting until the callee provides the requested response. The communication is done through APIs exposed by respective microservices configured using protocols such as HTTP or gRPC.

  • In asynchronous communication, the calling thread is not blocked and one microservice may broadcast a message to one or multiple microservices without waiting for a response from any one of them. Such communication is achieved by using a message broker between services, such as RabbitMQ or Kafka.

Using an asynchronous messaging pattern in a microservice architecture may have some advantages of its own.

  1. Load Balancing: Suppose, multiple instances are running of a consumer microservice the producer can add requests to a queue and based on a scheduling algorithm, say, Round Robin, the queue provider can distribute the requests among the consumer microservice instances.

  2. Event-Driven Architecture: Asynchronous communication makes it possible to design event-driven applications. Using the publisher/subscriber model, a publishing microservice can broadcast an event to multiple microservices that have subscribed to a particular event.

  3. Loose Coupling: Since the sender and the receiver aren't synchronized, therefore the services are said to be loosely coupled.

  4. Fault Tolerance: When using a messaging queue, the sender can still queue messages even if the receiving service is down. This way one of the microservices being down won't affect other microservices. The unavailable microservice can resume consuming messages from the queue as soon as it recovers.

Even though using an asynchronous messaging pattern provides a lot of advantages over synchronous messaging, it comes with its challenges.

  1. Complexity: Implementing asynchronous messaging across multiple systems is a challenging task in itself.

  2. Coupling: While it reduces coupling among microservices, it increases the coupling between the systems and the message broker. Therefore, it may prove difficult to move to another messaging infrastructure later on in the development cycle.

  3. Latency: The time it takes to deliver a message might increase if there are too many messages pending in the queue.

  4. Cost: During high throughput, the messaging platform can incur significant costs.

Containerization & Orchestration

Containers and orchestrators play a vital role in the deployment of microservices as they address key problems and challenges associated with the deployment of microservices effectively and conveniently.

When deploying a monolith application, the complete application has to be deployed on a single server. This could lead to the complete application going down during the deployment process. Microservices on the other hand can be deployed individually, therefore, ensuring that the complete application doesn't go down when only a small service needs to be deployed.

Containers, such as Docker, help in deploying and scaling these microservices independently. Since containers provide a lightweight and consistent environment to package microservices and their dependencies, it eliminates the issue of the service working on one machine and failing on another. It also makes it easier to scale as we can scale a particular microservice based on the amount of load it is under.

Orchestrators on the other hand are responsible for managing and automating the deployment and scaling of these containerized microservies. Orchestration tools, like Kubernetes, simplify the management of complex distributed systems ensuring better fault tolerance, higher availability and efficient resource management.

By leveraging containerization and orchestration, organizations can effectively manage and operate large-scale microservices architectures, ensuring scalability, resilience, and efficient resource utilization while simplifying the deployment and management of the overall system.

How to decompose a system into Microservices?

So, after diving this deep into Microservice Architecture, a question naturally arises --how do we decompose a system into microservices? The answer to this question is the Single Responsibility Pattern that we discussed earlier. To decompose a system into smaller microservices, business capabilities should be identified. Each microservice should be defined in such a way that it only aims to provide a specific business capability.

For example, an ECommerce Store can be split into the following microservices based on the various business capabilities:

  • Catalog Management

  • Inventory Management

  • Order Management

  • Delivery & Logistic Management

  • User Management

  • Product Recommendations

Each of the above microservice can be assigned to different teams based on the expertise of a team in a particular domain. This leads to better-defined roles and responsibilities of a team and ensures a more stable system.

Advantages of using a Microservice Architecture

  1. Build and Deployment: In a monolith application, every time there's a change the complete application needs to be built and redeployed. This could usually take some time ranging from seconds to multiple minutes depending upon the size of the application. In the case of microservices, only the microservice that has a change needs to be redeployed. Since a microservice is a small subset of the entire system, it will always take less time than it would take to redeploy the whole system.

  2. Scalability: Scaling microservices is generally more cost and time efficient than scaling a complete application. In a monolith application, you can't directly scale individual components and the complete system needs to be scaled up when under load. On the other hand, if a microservice is reaching its threshold for load, new instances of only that particular microservice need to be deployed.

  3. Fault Tolerant: In the event of an error, only the affected microservice will go down instead of the possibility of the whole system going down in a monolith system. Suppose, if the payments microservice fails for an e-commerce app, the customers will still be able to browse the product catalogue since the cataloguing service would still be up and running. When the error is fixed only this particular service can be deployed instead of deploying the complete application.

  4. Modularity and Maintainability: Microservices promote modularity by breaking down a complex system into smaller, loosely coupled services. Each service focuses on a specific business capability or function, making it easier to understand, develop, test, and maintain. Changes and updates can be made to individual services without impacting the entire system.

  5. Technology Diversity and Polyglotism: Microservices allow different services to be built and implemented using diverse technologies, programming languages, and frameworks. This flexibility enables teams to choose the most suitable technology stack for each service, taking advantage of the strengths of different tools and libraries.

Limitations of Microservice Architecture

  1. Increased Complexity: Microservice is more complex than monolithic applications as multiple systems need to be managed simultaneously instead of just one. Managing the network protocols responsible for the communication between microservices also adds to the complexity.

  2. Testing & Debugging: Microservices are also difficult to test and debug as multiple systems need to be tested together. End to End testing becomes more challenging as coordinating between services can become difficult. Debugging also becomes challenging, as you cannot just hook up a debugger to a service like you would in a monolithic application. There could be an error that spans multiple service communications.

  3. DevOps Complexity: Since the app is split into multiple services, deploying it is not as simple as deploying a monolithic application. Deploying and managing a large number of services in a distributed environment requires more robust practices.

  4. Distributed System Challenges: Microservice Architecture is a distributed system and therefore relies on network communication between services to function. This comes with the challenges of a distributed system such as potential network failures, network latency and maintaining data consistency across distributed data stores.

  5. Learning Curve and Skill Set: Adopting microservices may require a learning curve for development teams, as they need to understand distributed system concepts, service communication protocols, and technologies related to microservices. Acquiring the necessary skills and expertise may take time and effort.

The business complexities, requirements and readiness should be properly gauged before adopting the microservices architecture. Granted, it has quite a few benefits over the monolithic application setup but it also comes with its own limitations. It's crucial to address the challenges effectively to maximize the advantages of a microservices architecture.

If you liked this article, check out my other articles here.

I'm also active on Twitter, feel free to connect and check out my latest tweets.

Did you find this article valuable?

Support Atulya Bist by becoming a sponsor. Any amount is appreciated!