Database per Service Pattern
In this tutorial, we are going to discuss the Design Patterns of Microservices architecture which is The Database per Service pattern. We will use these patterns and practices when designing microservice architecture.
Database per service pattern is the first of a series of tutorials on Microservice Design Patterns.
There are two main options for organizing the databases when using microservices architecture:
- Database per service
- Shared database
1. Database per service
One of the core characteristics of the microservices architecture is the loose coupling of services. For that reason every service must have its own databases, it can be polyglot persistence among microservices.
Let’s think about our e-commerce application. We will have Product — Ordering, and Shopping Cart microservices that each services data in their own databases. Any changes to one database don’t impact other microservices.
The service’s database can’t be accessed directly by other microservices. Each service’s persistent data can only be accessed via Rest APIs. So database per microservice provides many benefits, especially for evolving rapidly and supporting massive scale systems.
Data schema changes made easy without impacting other microservices
- Each database can scale independently
- Microservices Domain data is encapsulated within the service
- If one of the database server is down, this will not affect to other services
Also, Polyglot data persistence gives the ability to select the best-optimized storage needs per microservices.
So if we see the image that example of the microservice architecture of e-commerce application;
- The product microservice using NoSQL document database for storing catalog related data which is storing JSON objects to accommodate high-volumes of read operations.
- The shopping cart microservice using a distributed cache that supports its simple, key-value data store.
- The ordering microservice using a relational database to accommodate the rich relational structure of its underlying data.
Because of the ability of massive scale and high availability, NoSQL databases are getting high popularity and becoming widely used in an enterprise applications. Also, their schema-less structure gives flexibility to developments on microservices. We discuss NoSQL databases later in upcoming tutorials.
So we should evolve our architecture by applying new microservices patterns in order to accommodate business adaptations faster time-to-market and handle larger requests.
See the image that UI and MS communication is direct, and it seems hard to manage communications. We now should focus on microservices communications by applying the API GW pattern and evolving this architecture step by step.
Despite all of those benefits, there are some serious drawbacks and challenges regarding the database per service approach. As we mentioned earlier, each microservice can only access directly its own data store. Therefore, services need a communication method to exchange data. So, each service must provide a clear API.
Consequently, there is a need for a failure protection mechanism in case the communication fails. Let’s say we send payment requests from service A to service B. Service A awaits for the response to perform appropriate action based on the result. During that, service B goes offline. We need to handle the situation and inform service A about the result when B is back online. The circuit breaker mechanism can help out here.
2. Shared Database
A shared database is considered an anti-pattern. Although, it’s debatable. The point is that when using a shared database, the microservices lose their core properties: scalability, resilience, and independence. Therefore, a shared database is rarely used with microservices.
When a shared database seems to be the best option for the microservices project, we should rethink if we really need the microservices. Maybe the monolith would be the better choice. Let’s see how a shared database approach looks like:
The use cases of using a shared database with microservices aren’t common. An example could be a temporary state while migrating the monolith to microservices. The primary benefit of the shared database over per service is transaction management. There is no need to span the transactions over the services.
Another important thing is no need to exchange stored data between microservices. So, the API is simplified, and there is no problem with the consistency of data and state in case the communication fails. There are some serious drawbacks though.
Microservices with shared databases can’t easily scale. What is more, the database will be a single point of failure. Changes related to the database could impact multiple services. Besides, microservices won’t be independent in terms of development and deployment as they connect to and operate on the same database.