This is the 2nd post in a series on microservices architecture.
Microservices are small Autonomous services that work well together, modeled around a business domain.
— Sam Newman
Let’s take a closer look at the principles upon which microservices are built before diving into different design patterns.
1. Modeled around business domain
With the domain-driven design, service boundaries become more stable and it becomes easier to align to team organizations. Additionally, it makes it easier to recombine your services for different user interfaces.
2. Culture of automation
To manage the complexity generated by a large number of moving parts, automation is essential. With every code check-in, continuous delivery should be ingrained in the company culture, and the build pipeline should be ready for deployment after testing and UAT. API-driven machine provisioning, OS configuration, and custom image creation for platforms should all be automated.
3. Hide implementation details
Implementation details should be hidden so that services can change and evolve independently. In place of accessing data directly from databases, services should communicate with each other over APIs to find the necessary information.
4. Decentralize all the things
Each service should be able to take its own decisions and have a complete degree of autonomy therein. Services in distributed systems communicate through dumb middleware, such as RabbitMQ. By understanding the concepts of orchestration and choreography, we can also determine when a centralized approach will work and when a loosely coupled approach will.
Service Orchestration — In-service orchestration, a single centralized executable process (the orchestrator) coordinates interactions among different services. It is the orchestrator’s responsibility to invoke and combine services. A single endpoint describes all the relationships between the participating services. The orchestration also manages transactions between individual services. Service composition is centralized through orchestration.
Service Choreography — As part of the choreography of services, messages are exchanged, rules of interaction are established, and agreements are established between several participating services. Choreography is a decentralized approach to service composition.
5. Deploy independently
If you make a change to a component, you should be able to release that into production without having to change any other components. Having one service per isolated operating system increases the level of interdependence. Consider coexisting service versions if you need to make breaking changes.
6. Consumer first
The idea that your services exist to be called must be conceived from the outside in and not the inside out. Think from the perspective of the consumer before designing it, and have consumer-driven contracts. Using API documentation tools such as Swagger allows you to execute an example service against API documentation.
7. Isolate failure
By default, microservices are not reliable. Assuring that you isolate failures from independent components in order to prevent cascading failures so that systems are more resilient. Understand the cases of request timeout, bulkheads, and consider using circuit breakers.
- Timeout — When you wait too long to determine that the call failed, the entire system will slow down. In the case of a short timeout, a call that is working normally will still be considered a failure. It is possible that a downstream service is down without a timeout. This will cause the system to hang. So, set a timeout for all cross-process calls and a default timeout period.
- Bulkhead — It is a way to protect yourself from failure. There are many bulkheads to consider in the software architecture. We should, for example, use different connection pools for downstream connections, so that if one connection pool is exhausted, the remaining connections will not be affected. If downstream services run slowly in the future or the connection pool is exhausted, only that connection pool will be affected and other services will continue to operate normally.
- Circuit breaker — The cross-service calls can be protected by a circuit breaker. A circuit breaker opens when a certain number of downstream resource requests fail to meet a certain threshold. If the circuit breaker is open, the system will quickly fail. The client will send some requests to check whether the downstream service has been restored after some time. In case of a normal response, it will resend the request after health is restored.
8. Highly observable
Observable systems should produce sufficient inputs and logs to allow us to figure out what is actually happening. There should be standard monitoring standards, health check pages, centralized log, and stats aggregation, downstream monitoring, distributed tracing with correlation id, and semantic monitoring to understand the system thoroughly.
We will look at different microservices patterns in the next post.