laitimes

Microservice granularity challenges: Finding the right microservice size

author:Not bald programmer
Microservice granularity challenges: Finding the right microservice size

preface

In the microservices architecture style, microservices are typically designed to follow the SRP (Single Responsibility Principle) as a unit of software that is deployed independently, focusing on one thing and doing it to the extreme. As developers, we often tend to make the service as small as possible without thinking about why. This subjectivity about what is and isn't a single responsibility is where we developers tend to make mistakes when it comes to service granularity. To overcome this dilemma that development teams face with microservice size, it's critical to understand the granularity drivers.

granularity

In microservices, we have two concepts – modularity, which involves breaking down the system into independent parts, and granularity, which deals with the size of those independent parts.

Determining the right level of granularity—the size of the service—is one of the many pain points that we developers struggle with in a microservices architecture. Granularity is not defined by the number of classes or lines of code in a service, but by the responsibilities of the service - so there is confusion about finding the right way to grain a service.

The granularity of a service is divided into two opposing forces – the granular decoupler and the granular integrator.

Granular decoupler

When should I consider splitting a service into smaller parts?

Granular integrators

Microservice granularity challenges: Finding the right microservice size
When should I consider putting services back together?

Granular decoupler

Since we live in the age of microservices and nanoservices, most development teams tend to make the mistake of splitting services at random, ignoring the consequences that come with it. To find the right size, you should make trade-offs against different parameters and make informed decisions within the context and boundaries of your microservices.

Granular decoupling drivers provide guidance and rationale for when to split a service into smaller parts. Let's look at how these drivers affect the size of a microservice, using an example.

Example: Consider a typical notification service that does three things: notify customers via SMS, email, or postal mail.

Microservice granularity challenges: Finding the right microservice size

Let's analyze this scenario on the decoupling drivers and find the right size. We start with:

Scope and functionality of services

Is the service doing too many irrelevant things? Scope and functionality are primarily determined by two attributes – the first is cohesion, which refers to the degree and manner of interrelationships between specific service operations. The second is the overall size of the component, which is typically measured in terms of the number of responsibilities, the number of entry points to the service, or a combination of both. Scenario: Observe the notification service, which one might say split into three separate single-duty services. But is this the right thing to do? The answer is no! Because this service has relatively strong cohesion, i.e., all these functions are related to one thing, which is notifications, and have a single purpose. So, there is no need to split this service, it should be one service to do three things. Next up:

Code volatility

Microservice granularity challenges: Finding the right microservice size
Microservice granularity challenges: Finding the right microservice size
Are the changes limited to a portion of the service? Code volatility is the frequency of source code changes. We must measure the frequency of code changes in the service to justify why the service should be split. Scenario: Suppose we have metrics for the following service capabilities:

Now, if we look at the metrics of the change, the frequent changes to the mailing mail notification section also require testing the SMS and email sections as a single service, which increases the scope of testing and deployment risk. So how do we solve this problem?

If we split this service into two separate services, e-notifications and mail-in notifications, frequent changes are now isolated in their own services, reducing the scope of testing and reducing deployment risk.

Scalability and throughput

Microservice granularity challenges: Finding the right microservice size
Microservice granularity challenges: Finding the right microservice size
Does the part of the service require different scalability? The scalability requirements of different service functions can be objectively measured to quantify whether the service should be split or not. Scenario: Consider the notification service example again, and measure the scalability requirements of a single service as follows:

In this case, as a single service, the email and mailing functions must be unnecessarily scaled to meet the needs of SMS notifications, impacting cost and resiliency (e.g., MTTS, i.e., mean time to start). This makes perfect sense to split the notification service into separate services—SMS, email, and letters—because it allows each service to scale independently to meet their different throughput needs.

Fault tolerance

Is there a bug that is causing a critical feature of the service to fail? The ability of an application to continue to run within a specific domain, even after a fatal crash (such as OOM). Scenario: Consider our notification service scenario, if the email function continues to have OOM errors and crashes fatally, the entire integration service will be paralyzed, including SMS and mailing mail processing. Splitting this single integrated notification service into three separate services provides some fault tolerance in the customer notification space. Therefore, a fatal error in the email feature does not affect text messages or mailed letters. Further explanation: Now, there may be a question here because the email feature crashes frequently, why not merge the SMS and mail mail features? It's a valid question. If we remember, when we discuss the code volatility scenario, we separate mailed letters from emails and text messages and merge them into one – electronic notifications. If we can do that there, we can do it here. So, why not? Because email and text messages are related, they are both electronic notification methods. But here, SMS and mailing notifications have nothing in common to merge them. In other words, there is no cohesion here.
Note: Remember, if a service is difficult to name because it's doing multiple unrelated things, then consider splitting the service. Second, whenever a service is split, regardless of the driver, always check to see if you can form strong cohesion with the "leftover" functionality. So, it makes sense here to split the notification service into three separate services. The last drivers are:

Scalability

Is the service always expanding to add new features? The ability to add extra functionality as the service expands. Scenario: Let's say we have new features that we want to add to a notification service – such as mobile push notifications, desktop notifications, social media notifications, etc. These new features can certainly be added to a single, consolidated notification service. However, each time a new notification is added, the entire notification service needs to be tested, and all notification features need to be deployed to production unnecessarily.
Microservice granularity challenges: Finding the right microservice size
Note: This scenario is only applied if you know in advance about the plan and the additional consolidation features you want to be part of the domain.

Recommended Practices

  1. If a service is difficult to name because it's doing multiple unrelated things, then consider splitting the service.
  2. Whenever a service is split, regardless of the driver, always check to see if you can form strong cohesion with the "leftover" functionality.
  3. Split services based on business capabilities rather than technical capabilities.
  4. Use the Single Responsibility Principle (SRP) when designing microservices, but keep in mind the panorama of strong cohesion.
  5. Use decoupling drivers to analyze the trade-offs of splitting services.
Microservice granularity challenges: Finding the right microservice size

Read on