Thoughtfully weighing the benefits as well as drawbacks of the microservices architecture relative to a specific application & organization is a key decision element when considering architecture. This blog aims to outline key best practices when designing microservices architecture and how these best practices accelerate engineering efficiency.
Microservices architecture is a method that structures an application as a collection of services with the following characteristics:
- Testable and maintainable
- Self-sufficiently deployable
- Formed and organized around business abilities
- Owned and managed by a small team
Microservices architecture signifies many small, programmed, and self-contained services that carry out a single business operation. It facilitates speedy, periodic, and dependable delivery of large and complex applications.
Benefits of a Microservices Architecture
Here are a few advantages of microservices architecture:
- It allows the creation of a microservice in the language of your choice, released at your speed, and measured per your benchmark.
- Since microservices are developed independently by different teams, development and marketing can be done simultaneously.
- Errors and fault identification does not impact the whole digital ecosystem of the organization.
Traditional Microservice Architecture
The planning phase: During the planning phase, it is important to analyse if the microservices architecture is a good fit based on requirements. This can be done by segmenting requirements into functions that provide value. This helps verify that the application can be subdivided into microservices while still retaining its core features and operability.
Get everyone on board with the idea: The transition from monolithic architecture to microservices is initially a lengthy and tedious process—and the impact goes beyond the development team. Stakeholders should consider the time, money, and technical expertise required for the infrastructural changes that must be adopted. The engineering team specifically faces considerable disruption being both the implementer and the user throughout the transition.
Build teams around microservices: Each microservice acts as an independent application in itself, so teams should, too. Form separate teams for handling different microservices. This also requires that such teams have the necessary skills and tools to develop, deploy, and manage a specific service on their own. The teams should be versatile and big enough to handle their operations autonomously without wasting time communicating.
Best Practices to Design a Microservice Architecture
Here are some of the best practices to design microservice architecture:
Single Responsibility Principle
-Differentiate your microservices from your business functions and services.
This will help you avoid building microservices that are either too large or too small. If the former occurs, you will see no benefits from using the microservice architecture. The latter will lead to an exponential increase in operational costs that outweighs any benefits gained.
- Design your services to be loosely coupled, have high cohesion, and cover a single bounded context
A loosely coupled service depends minimally on other services. Having high cohesion requires that the design of the service should follow the single responsibility principle—that is, it should perform only one main function and do it well. Lastly, design your service such that they are domain-specific while containing internal details of the domain and domain-specific models. This ensures that a microservice covers a single-bounded context, achieving a Domain-Driven Design (DDD).
- Use asynchronous communication to achieve loose coupling
To avoid building a mesh of tightly coupled components, consider using asynchronous communication between microservices.
Separate data storage for each microservice
It defeats the purpose of having microservices if you are using a monolithic database that all your microservices share. Any change or downtime to that database would then impact all the microservices that use the database. Choose the right database for your microservice needs, customize the infrastructure and storage to the data that it maintains, and let it be exclusive to your microservice. Ideally, any other microservice that needs access to that data would only access it through the APIs that the microservice with write access has exposed.
If your microservice is dependent on another system to provide a response, and that system takes forever to respond, your overall response SLAs will be impacted. To avoid this scenario and quickly respond, one simple microservices best practice you can follow is to use a circuit breaker to timeout the external call and return a default response or an error. This will isolate the failing services that your service is dependent on without causing cascade failures, keeping your microservice in good health. You can choose to use popular products like Hystrix/Resilence4J. This is better than using the HTTP CONNECT_TIMEOUT and READ_TIMEOUT settings as it does not spin up additional threads beyond what’s been configured.
Proxy via API Gateway
Instead of every microservice in the system performing the functions of API authentication, request / response logging, and throttling, having an API gateway doing these for you upfront will add a lot of value. Clients calling your microservices will connect to the API Gateway instead of directly calling your service. This way you will avoid making all those additional calls from your microservice and the internal URLs of your service would be hidden, giving you the flexibility to redirect the traffic from the API Gateway to a newer version of your service. This is of a higher priority when a third party is accessing your service, as you can throttle the incoming traffic and reject unauthorized requests from the API gateway before they reach your microservice. You can also choose to have a separate API gateway that accepts traffic from external networks.
API Backwards Compatibility
You can safely introduce changes to your API and release them fast as long as they don’t break existing callers. One possible option is to notify your callers, have them provide a sign off for your changes by doing integration testing. However, this is expensive, as all the dependencies need to line up in an environment and it will slow you down with a lot of coordination. A better option is to adopt contract testing for your APIs. The consumers of your APIs provide contracts on their expected response from your API. You as a provider would integrate those contract tests as part of your builds and these will safeguard against breaking changes. The consumer can test against the stubs that you publish as part of the consumer builds. This way you can go to production faster with independently testing your contract changes.
It's not always possible to make backwards compatible changes. When you are making a breaking change, expose a new version of your endpoint while continuing to support older versions. Consumers can choose to use the new version at their convenience. However, having too many versions of your API can create a nightmare for those maintaining the code. Hence, have a disciplined approach to deprecate older versions by working with your clients or internally rerouting the traffic to the newer versions.
Scalability is a key factor when you design microservices. Whether you create a new service or enhance existing services, you need to consider various approaches and techniques for this. Ex: Caching.
Authentication & Authorization
Handling user authentication and authorization varies significantly between microservices and monolithic applications.
E.g., JSON Web Token, OAuth2
Sometimes you need to implement external configurations like credentials along with the microservice architecture pattern. You might need to monitor metrics to learn how the application performs. Consider using a service mesh to manage communications between services.
As an end-user action triggers application work, API calls and service work cascade down the application topology, and a single action may result in tens, or hundreds, of monitorable events. Trying to manually correlate errors across a service cascade is nearly impossible, so use a monitoring system that can discover and display events based on a common timeline to support root cause analysis.
Most microservices monitoring systems place a monitoring agent on each service instance, where it can track specific instance data. These monitoring systems can also capture application-created log information. All of this data migrates to a centralized database, where the system does cross-correlation, allowing monitoring alerts or humans to track important event data.
Use RESTful APIs Optimally
The REST (Representational State Transfer) APIs can work wonders for microservices as developers need not install any additional software or libraries when creating a REST API. At the same time, they provide a great deal of flexibility since the data is not tied to any particular method or resource. The result is an ability to handle multiple types of calls, return different data formats, and alter the structure with the correct implementation of hypermedia.
You can have the best designed microservice meeting all the checks, but with a bad design of the hosting platform it would still behave poorly. Isolate your microservice infrastructure from other components to get fault isolation and best performance. It is also important to isolate the infrastructure of the components that your microservice depends on.
Separate Release Train
Your microservice needs to have its own separate release vehicle which is not tied to other components within your organization. This way you are not stepping on each other’s toes and wasting time coordinating with multiple teams.
While microservices give you the freedom to develop and release independently, certain standards need to be followed for cross-cutting concerns so that every team doesn’t spend time creating unique solutions for these. This is very important in a distributed architecture such as microservices, where you need to be able to connect all the pieces of the puzzle to see a holistic picture. Hence, enterprise solutions are necessary for API security, log aggregation, monitoring, API documentation, secrets management, config management, distributed tracing, etc.
Is a Microservices Architecture Convenient for You?
Before changing your system to microservices, it is vital to understand why you need to do it. Analyze your system and study the distinctive features in your system and notice which part of the system troubles you the most. At an early stage, consider a less critical part of the system and evaluate its functions as a microservice.
In addition to these microservices best practices, you also need to make sure that the project manager can handle end-to-end service-oriented architecture migrations and development. Only businesses who understand the nuances of the cultural shift towards microservices will leverage the technology to its full potential.
The modular nature of microservices architecture creates benefits related to flexibility, scalability, quality & maintainability. When best practices are adhered to and care is taken to ensure that the microservices landscape doesn’t end up being overly complex, substantial return on investment accrues.
Encora understands that the choice of architecture can impact the viability of the application or platform. Our engineers ensure that enough time is spent on understanding our client’s performance, cost, timeline & scalability needs, before arriving at the right architecture. We also deeply appreciate the challenges associated with culture change that must accompany architectural pivots.
Advantages of a Microservices Architecture:
• It allows the creation of a microservice in the language of your choice, released at your speed, and measured per your benchmark.
• Since microservices are developed independently by different teams, development and marketing can be done simultaneously.
• Errors and fault identification does not impact the whole digital ecosystem of the organization.
Let's talk and work together!