Microservice Architecture

Photo by Vincent Camacho on Unsplash

“Simplicity is the soul of Efficiency.”― Austin Freeman

Microservice came into the dev world after 2000, where some internet giants like Netflix, Amazon, Google are getting bigger and bigger to serve people all around the world. Every company running on software platforms and based on software platforms that start growing the time as company-wise it should widen its capabilities to a larger client base, various sets of features, and scalability for larger geographical zones.

Now let’s look at the time where the application was built in the older days all the components in the software are integrated with the codebase grows exponentially growth same as the business getting scale or business functionalities are being added. if we consider from every stakeholder's aspect of view
Developers are getting hard to write codes and find the bugs. Testing makes harder due to the highly coupled manner which can cause other components to be affected. From the Management’s perspective to survive and compete in the current business world, fast adaption and response are needed but when their application becomes bottlenecks customers tend to move into a better solution. As a solution for this Microservices is adapted by the organizations.

We discussed the problems in the monolith architecture and what are the disadvantages of using monolith applications in large enterprise software. Now we have to identify what is Microservice architecture and how it solves the problems in monolith applications.

What are Microservices?

Loosely Couple - As discussed the larger system is now broken as a modular approach and each of these services are now can work independently without having to depend on other services.

Tight Coupled and Loosely Coupled (Image from Info.nl)

This makes the developer has to focus on a specific area and have better control over the development process.

Highly Maintainable and Testable - Due to the modularized approach of the system scope is now clear and has an exact boundary and a clear understanding of the responsibilities and functionalities within all teams. Due to this when it comes to bug fixing, new feature adding, or refactoring there is less codebase when it compares to the monolith application which makes the service is easy to maintain and keeps a clean codebase. And when the maintenance is done. And also in a modification, there is no need to test the entire system just the related services, and modified service is enough.

Independently Deployable & Scale - Services of the microservice application have zero dependencies with other services when it comes to the deployment which makes it possible to independently deployable separate components without having to affect or taking down the entire system offline.

Independent Deployment Based on Containers (Image from NGINX)

And also these deployments are going separately and independently services can scale freely, As an example during a certain season like Black Friday, e-commerce websites can gain a vast amount of requests during that period especially processing orders and checkout due to the modularity scalable only the necessary services and necessary times and then scale out when not needed this helps the business processes run smoothly and fewer people intervention due to the automation of tasks.

Based on Domain Driven Design - When defining a microservice it is based on exact defined scope, this is an important factor of achieving the modularity, what domain-driven design is it identifies the boundaries, processes, and rules of a specific domain and implement the functionalities which are only relevant to that domain as an example a “Payment Processor” service only process the payment handling and not the calculation of prices checkout from the carts in an e-commerce application. This factor brings the “Do one thing and Do it well!” principle.

Maintained By Smaller Teams - The disadvantage of larger codebases are the conflicts of maintaining and keeping proper track of the modification. But in microservices development teams are smaller in size working independently from each other teams. The “Two Pizza Rule” is the most famous example of how many members should be in the team for maximum productivity and the entire team should be fed with two pizzas. And each member of the team has a better understanding of the services that they are working on.

Although the services are distributed to deliver the expected results the application should be communicating with each other using lightweight mechanisms like HTTP/HTTPS. using mostly REST APIs.

Due to those characteristics, Microservice brings lots of advantages one is the cost for an organization where the ability to scale and scale out based on the demand. and also it provides technology freedom based on the requirement by providing abilities like polyglot development for the most suitable language and technology stack (As an example using Machine Learning and Data Science-based service developing using python and Payment or Financial service developing using Java)

Scalability of the Microservices

The Scale Cube (Image from Microservices.io)

X-Axis - The application scaling through the X-Axis means that it should be able to produce copies of its own instances till it meets the demand.

Y-Axis -t Scaling through the Y-Axis called “functional decomposition”. it is about choosing the correct granularity level of decomposing the relating functionalities into services. It is important that to identify the right boundary by using the Domain-Driven Design approach otherwise too much granularity may break the application because even communicating with different services takes time. and when in need the necessary services can only scale.

Z-Axis -t Scaling through the Z-Axis is about Sharding. In Sharding rather than using one DB based it distributed the traffic among multiple databases, this can even be optimized using sharding based on different geographical areas.

Best Practices of Using Microservices Architecture

Always Based on Domain Driven Design - As discussed above it is important to have a clear idea about the boundaries of services and making sure the functionalities inside a certain service are required by that service itself and not by another service.

Avoid Using Hardcoded Values - The application might contain values that are used as configuration or other important values, for example, environment variables, service names, ports. For the easiness of developing, developers tend to hardcode these values, but microservice are changing based on factors (service may restart, scaled-out, etc, ports are getting changed) these may cause a complete break of the system.

Using Appropriate Design Patterns - Due to the dynamic behavior of the services in microservice patterns it is difficult to control or keep track of the services at the current time using each time and direct traffics manually or programmatically, To solve these issue Microservice has patterns like Circuit Breaker Pattern to deal with the fault tolerance and driver the traffic into another appropriate service and also Service Discovery and Service Registry to keep track of the up and running services and their information including hostnames, port names.

Use appropriate Level of Logs - Loggin is an important mechanism to debug and find the errors in the productions. When logging the details it is better to follow the “Fail Fast Log Later” approach. What this does is it does not log the error at the level of exception that took place it moved to the next level, and then to the next level and when it reaches the last layer it will log the error. (Ex - Repository -> Service -> Controller ). this will avoid login the same error twice. And when logging it is important to keep the necessary details as a stack trace in order o trace back and find where the error actually happened such as timestamps, unique request IDs, and proper log levels (DEBUG, ERROR, INFO, WARN).

Versioning Services - In microservice architecture the compatibility should maintain among the different services to communicate with each other but due to the updates there might be issues for each time there is an ongoing update or bug fix it is important to keep the proper versioning system like semantic version as below.

Semantic Versioning (Image from Devopedia)

This provides the ability to manage the services without breaking, and when it comes to deploying the versions it is always better to automate in isolated environments just like containers.

Using Identity Servers - When communicating with the different services it is important to do the authentication and authorization for ensures security. But consuming more than one service leads to a waste of latency because each request should validate. As a solution for this developers can direct all the requests coming to the service layer into an identity service and then do the validation and redirect the request into the appropriate service.

Maintaining Proper Documentation - When implementing other services or trying to communicate with the other services developers have an understanding of how to communicate, endpoints, parameters, and functionality rather than writing from the scratch tools like Swagger based on the OpenAPI specification provides a programmatical way of creating API documentations.

For more information about microservices find the link below!

References

Software Engineer