Microservices Patterns: API Composition and CQRS Patterns
Microservices Patterns Series — Part 07
In the typical microservices design, the Database per service pattern indicates that each microservice should be responsible for its own data. However, if the consumer wants to access data, which spans across multiple microservices, we should be able to find a solution for that. There are two data query patterns, which we can introduce here.
- The API Composition Pattern [1]
- The Command Query Responsibility Pattern (CQRS) [2]
Out of these two patterns, the API Composition Pattern is more simpler to use and adopt. Comparatively, The CQRS Pattern is more powerful and complex. In this blog, lets compare these two patterns in detail.
The API Composition Pattern
The API Composition Pattern: Implement a query that retrieves data from several services by querying each service via its API and combining the results. [1]
Structurally the API Composition Pattern deals with two key participants. (1. The API Composer 2. The Provider Services / Microservices).
The API Composer implements the query operation by querying the provider services.
The API Composer invokes all involved provider services and combines the results together with an in-memory join before presenting to the consumer.
In the API Composition pattern, these provider service invocations could happen in parallel or in sequence. For example, in figure 01, all three provider services (order, payment, deliver) should be called in sequence or in parallel (in a quick time) in order to get an output.
API Composition Pattern — Drawbacks
- The overhead of invoking multiple service providers using multiple database queries can be time consuming. In such a situation, more computing and networking resources are required and that would add some additional cost as well to the application in general.
- Having multiple service endpoints can be a hindrance to the overall availability of the application and increases the coupling with related service endpoints. There can be situations, where at least one failed service endpoint, which would result a less reliable system.
- The data inconsistencies could be there in the API composer result due to the fact that, API composer does retrieve data from multiple external service providers. Each service provider can have data with inconsistencies attached to them. For example as in figure 01 example, if a consumer decides to cancel an already placed order, which the consumer had completed a few minutes ago, the Order microservices is now updated as a CANCELLED order. However, with eventual consistency around there could be a possibility that the Purchasing microservice is not yet updated with a payment cancellation, which could lead to an inconsistency of the final data query output via the API composer. In addition to that, sometimes certain service provider queries need data from other service providers to respond. In such situations, having data replicas in many service provider level databases are required. Maintaining multiple data replicas of other service providers could lead to data inconsistencies as well.
- The inner joins, which are being used by the API composer can be quite confusing at times and difficult to apply.
CQRS (Command and Query Responsibility Segregation) Pattern
The CQRS Pattern: Implement a query that needs data from several services by using events to maintain a read only view that replicates data from the services
In order to overcome some of the drawbacks, which have been identified under the API Composition Pattern, the CQRS Pattern can be applied to many microservices based applications.
So, what can CQRS can do for us?
In simple terms, it is all about segregation or the separation of commands and queries. As you see in figure 02, CUD (Create, Update and Delete) operations are routed through the domain / command model and the R(Read) operations are routed through the query model.
Furthermore, the command model publishes domain events whenever its data changes to the query model as an eventual consistency transaction via an Event Bus.
With this approach, all the data queries pertaining to the given service is accommodated via the Query DB and will “not” be depending on the Command level DB anymore. This will certainly improve the performance and the scalability of the application.
Conclusion
Hope the above simple comparison will give you the importance of the CQRS method and its application primarily on a microservices architecture compared to the API composition pattern, which we mostly tend to implement.
Thank You!
References
- API Composition Pattern: https://microservices.io/patterns/data/api-composition.html
- CQRS Pattern: https://microservices.io/patterns/data/cqrs.html
- Database per Service Pattern: https://microservices.io/patterns/data/database-per-service.html