Data is the primary requirement of any software. Thus, efficient and effective data management can make or break a business. For starters, you have to ensure that data is available to the end user at the right time. Monolithic systems are notorious for their complex handling of data management. In contrast, microservices architecture paints a different picture. Here are some of data management patterns for this type of architecture.
Database Per Service
In this model, data is managed separately by each microservice. This means that one microservice cannot access or use the data of another one directly. In order to exchange data or communicate with each other, a number of well-designed APIs are required by the microservices.
However, the pattern is one of the trickiest to implement. Applications are not always properly demarcated. Microservices require a continuous exchange of data to apply the logic. As a result, spaghetti-like interactions develop with different application services.
The pattern’s success is reliant on carefully specifying the application’s bounded content. While this is easier in newer applications, large systems present a major problem at hand.
Among the challenges of the pattern, one is to implement queries that can result in the exposure of data for various bounded contexts. Other options include the implementation of business transactions that cover multiple microservices.
When this pattern is applied correctly, the notable benefit of it includes loose coupling for microservices. In this way, your application can be saved from the impact-analysis-hell. Moreover, it helps in the individual scaling up of microservices. It is flexible for software architects to select a certain DB solution while working with a specific service.
When the complexity of database per service is too high, then a shared database can be a good option. A shared database is used to resolve similar issues while using a more relaxed approached as a single database receives access by several microservices. Usually, this pattern is considered safe for developers because they can make use of existing techniques. Conversely, doing such always restricts them from using microservices at its best. Software architects from separate teams require cooperation to modify the schema of tables. It is possible that the runtime conflicts occur in case two or more services attempt using a single database resource.
In a microservices architecture, while working with the implementation of complex queries, API composition can be one of the best solutions. It helps in the invocation of microservices for the needed arrangement. When results are fetched, a join (in-memory) is executed of the data after which the consumer receives it. The pattern’s drawback is its utilization of in-memory joins—particularly when they are unnecessary—for bigger datasets.
Command Query Responsibility Segregation
Command Query Responsibility Segregation (CQRS) becomes useful while dealing with the API composition’s issues.
In this pattern, the domain events of microservices are ‘listened’ by an application which then updates the query or view database accordingly. Such a database can allow you to handle those aggregation queries which are deemed complex. It is also possible to go with the performance optimization and go for the scaling up of the query microservices.
On the flipside, this pattern is known for adding more complexity. Suddenly it forces that all the events should be managed by the microservice. As a consequence, it is prone to get latency issues as the view DB exercises consistency in the end. The duplication of code increases in this pattern.
Event sourcing is used in order to update the DB and publish an event atomically. In this pattern, the entity’s state or the entity’s aggregate in the form of events—where states continue to change—are stored. Insert and update operations cause the generation of a new event. Events are stored in the event store.
This pattern can be used in tandem with the command query responsibility segregation. Such a combination can help you fix issues related to the maintenance of data and event handling. On the other hand, it has a shortcoming as well; the imposition of an unconventional programming style. Moreover, eventually, the data is consistent, not always the best factor for all the applications.
When business transactions extend over several microservices, then the saga pattern is one of the best data management patterns in a microservices architecture. A saga can be seen as simply local transactions—in a sequence or order. When Saga is used to perform a transaction, an event is published by its service. Consequently, other transactions follow after being invoked due to the prior transaction’s output. In case, failure arises for any of the chain’s transactions, a number of transactions (as compensation) are executed by the Saga to repair the previous transactions’ effect.
In order to see how Saga works, let’s consider an instance. Consider an app which is used for food delivery. If a customer requests for an order of food, then the following steps happen.
- The service of ‘orders’ generates an order. In this specific time period, a pending state marks the order. The events chain is managed by a saga.
- The service of ‘restaurant’ is contacted by the saga.
- The service of ‘restaurant’ begins the process to start the order for the selected eatery. When the eatery confirms, a response is generated and sent back.
- The response is received by the Saga. Considering the response contents, it can either proceed with the approval or rejection of the order.
- The service of ‘orders’ modifies the order state accordingly. In the scenario of the order approval, the customer receives the relevant details. In the scenario of order rejection, the customer receives the bad news in the form of an apology.
By now, you might have realized that such an approach is too distinct from the point-to-point strategy. While this pattern may add complexity, it is a highly formidable solution to respond to several tricky issues. Though, it is best to use it occasionally.