Overview
The Self-contained Service pattern resolves the problem of microservices that need collaboration from other microservices to respond to synchronous client requests, making the microservice independent. It eliminates synchronous inter-service communications by replacing them with asynchronous ones.
Problem
Consider an example in a food delivery business, with a system composed of: an Orders microservice (responsible for creating orders), a Restaurant microservice (menus and prices), a Customer microservice (customer status), a Kitchen microservice (kitchen tickets), and a Payments microservice (credit card authorisation).
A client makes a synchronous HTTP POST /orders request to the Orders microservice, expecting an immediate response (approximately 600 ms). Although creating an order is the responsibility of the Orders microservice, it requires collaboration from other microservices.
Without the pattern, collaboration with other microservices happens through synchronous communications. Using synchronous communications between microservices reduces their availability, since they may be unavailable — causing the order not to be created and returning an error to the client. This means the entire feature of creating an order is unavailable whenever any collaborating service is down. There is also the problem of slow response times due to active waiting between microservices and with the client.
Solution
The solution consists of eliminating all synchronous communications between the Orders microservice (the independent microservice) and its collaborators, using other patterns such as Saga and CQRS.
After asynchronously invoking the first collaborator (Consumer Service), the Orders microservice can respond immediately to the client that the process has been initiated successfully but is not yet complete — returning HTTP 202 and the necessary data. The status of the process can then be queried by the client, or the system can notify the client of the progress.
Trade-offs
Benefits
- Increased availability of microservices — with asynchronous communications the Orders microservice does not wait for collaborators' responses, making it more independent
- Asynchronous communication allows the client to perform other tasks instead of waiting for the response — particularly beneficial for time-consuming operations
Drawbacks
- Increased complexity and cost when applying the CQRS and Saga patterns
- Using Saga makes the communication more complex to implement compared to a simple HTTP call
- More functionality must be developed to apply the pattern, instead of focusing solely on the microservice's core requirements
When to Use
Apply this pattern when:
- A microservice must collaborate synchronously with several others to handle a client request, causing tight availability coupling
- Response time and availability are critical requirements
- The team is already using Saga and CQRS, which are prerequisites for this pattern
Avoid when:
- The microservice can fulfil client requests without collaborating with other services — the pattern adds cost and complexity that is not justified
- The team is not yet experienced with Saga and CQRS, which are required dependencies