← Back to Questions
Microservices

Explain Database Per Service Pattern?

Learn Explain Database Per Service Pattern? with simple explanations, real-time examples, interview tips and practical use cases.

What is Database Per Service Pattern in Microservices?

Database Per Service Pattern is a design pattern used in Microservices Architecture where each microservice owns and manages its own separate database.

Instead of sharing one common database among all services, every microservice maintains its own private database that cannot be directly accessed by other services.

This pattern is one of the most important best practices in Microservices Architecture because it helps achieve:

  • Loose coupling
  • Independent deployment
  • Independent scalability
  • Better fault isolation
  • Service autonomy

Simple Understanding of Database Per Service Pattern

Imagine a large company with different departments:

  • HR Department
  • Finance Department
  • Sales Department
  • Support Department

Each department maintains its own records and files independently.

The Finance Department should not directly modify HR files. If information is needed, departments communicate officially.

Similarly, in Microservices Architecture:

  • User Service maintains User Database
  • Order Service maintains Order Database
  • Payment Service maintains Payment Database

Services should communicate using APIs or events instead of directly accessing other databases.


Why Database Per Service Pattern is Needed

In traditional Monolithic Architecture, all modules usually share one database.

Monolithic Example

----------------------------------------------------
|                 Shared Database                  |
----------------------------------------------------
| Users | Orders | Payments | Courses | Products |
----------------------------------------------------

Problems:

  • Tight coupling between modules
  • Difficult schema changes
  • Deployment dependency issues
  • Scalability limitations
  • High risk of system-wide impact

To solve these problems, Microservices introduced Database Per Service Pattern.


Database Per Service Architecture

-----------------------------------------------------
|                   API Gateway                    |
-----------------------------------------------------
        |                |                 |
        v                v                 v

-----------------------------------------------------
| User Service | Order Service | Payment Service   |
-----------------------------------------------------
        |                |                 |
        v                v                 v

-----------------------------------------------------
| User DB     | Order DB     | Payment DB         |
-----------------------------------------------------

Each service owns its own database independently.


Main Rule of Database Per Service Pattern

The most important rule is:

A microservice should directly access only its own database.

Other services must communicate through:

  • REST APIs
  • Feign Client
  • Kafka events
  • RabbitMQ messages

Example Without Database Per Service Pattern

Suppose all services share one database:

---------------------------------------------------
|                 Shared Database                 |
---------------------------------------------------
| Users | Orders | Payments | Notifications      |
---------------------------------------------------

Problems:

  • Payment Service may directly modify Order tables
  • Schema changes affect all services
  • Tight dependency between services
  • Difficult maintenance

Example With Database Per Service Pattern

User Service       ---> User Database

Order Service      ---> Order Database

Payment Service    ---> Payment Database

Notification Service ---> Notification Database

Each service independently manages its own data.


Real-Time Example

Suppose an online learning platform contains:

  • Auth Service
  • Course Service
  • Payment Service
  • Interview Service

Database Structure

Auth Service       ---> auth_db

Course Service     ---> course_db

Payment Service    ---> payment_db

Interview Service  ---> interview_db

Each service independently maintains its own schema and database logic.


How Services Communicate with Separate Databases

Suppose Order Service needs payment details.

Wrong Approach:

Order Service ---> Directly Access Payment DB

Correct Approach:

Order Service ---> Payment Service API

Payment Service internally accesses its own database and returns the response.


Advantages of Database Per Service Pattern

1. Loose Coupling

Services become independent because they do not directly depend on other databases.


2. Independent Deployment

Database schema changes affect only one service.


3. Better Scalability

Each database can scale independently.

Example

Payment Service may require high-performance database scaling while Notification Service may not.


4. Technology Flexibility

Different services can use different databases.

Example

User Service        -> MySQL

Analytics Service   -> MongoDB

Cache Service       -> Redis

5. Better Fault Isolation

Database failure in one service usually does not affect other services.


6. Improved Security

Services cannot directly modify another service database.


7. Easier Maintenance

Smaller independent databases are easier to manage.


Challenges of Database Per Service Pattern

1. Distributed Transactions

Managing transactions across multiple databases becomes difficult.

Example

  • Order created successfully
  • Payment failed

Rollback becomes complicated.

Solution

  • Saga Pattern
  • Event-driven architecture

2. Data Duplication

Some data may exist in multiple services.


3. Complex Reporting

Generating reports across multiple databases becomes difficult.


4. Eventual Consistency

Data synchronization between services may not happen instantly.


5. Increased Infrastructure Complexity

Managing many databases increases operational complexity.


How Services Share Data

Since databases are isolated, services share information using:

  • REST APIs
  • Feign Client
  • Kafka events
  • RabbitMQ messaging

Example Using REST API

Order Service
      |
      v
Payment Service API
      |
      v
Payment Database

Example Using Event-Driven Architecture

Order Created Event
          |
          v
        Kafka
          |
          v
Payment Service

Services communicate asynchronously using events.


Database Types Used in Microservices

Database Usage
MySQL Relational transactions
PostgreSQL Advanced SQL support
MongoDB Document-based storage
Redis Caching
Cassandra Large-scale distributed storage

Database Per Service vs Shared Database

Feature Shared Database Database Per Service
Coupling Tightly coupled Loosely coupled
Scalability Limited Independent scaling
Deployment Dependent Independent
Technology Flexibility Limited High flexibility
Fault Isolation Poor Better
Complexity Simple initially More complex

Best Practices for Database Per Service Pattern

  • Never allow direct database access across services
  • Use APIs or events for communication
  • Implement Saga Pattern for distributed transactions
  • Use proper monitoring and backups
  • Secure databases independently
  • Enable database scalability where needed

Real-Time Company Example

Netflix uses Database Per Service Pattern extensively.

Different services maintain separate databases for:

  • User profiles
  • Recommendations
  • Streaming history
  • Billing

This allows Netflix to scale and deploy services independently.


Interview Ready Answer

Database Per Service Pattern is a Microservices design pattern where each microservice owns and manages its own separate database. Other services cannot directly access that database and must communicate using APIs or messaging systems. This pattern helps achieve loose coupling, independent deployment, scalability, technology flexibility, and fault isolation. However, it also introduces challenges such as distributed transactions, eventual consistency, and increased operational complexity.


Frequently Asked Questions

Why should each microservice have its own database?

To achieve loose coupling, independent deployment, and better scalability.

Can one service directly access another service database?

No. Services should communicate using APIs or messaging systems instead of direct database access.

What is the biggest challenge in Database Per Service Pattern?

Managing distributed transactions and maintaining data consistency.

Can different microservices use different databases?

Yes. Each service can choose the database best suited for its requirements.

Which patterns help manage distributed transactions?

Saga Pattern and Event-Driven Architecture are commonly used.

Database Per Service Pattern Coding Example

In this example, we will create two microservices:

  • Order Service with order_db
  • Payment Service with payment_db

Order Service will not directly access Payment Database. Instead, Order Service will call Payment Service using Feign Client.


Architecture

Order Service
     |
     | Feign Client API Call
     v
Payment Service
     |
     v
Payment Database

1. Order Service Database Configuration

spring.application.name=ORDER-SERVICE
server.port=8081

spring.datasource.url=jdbc:mysql://localhost:3306/order_db
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

2. Payment Service Database Configuration

spring.application.name=PAYMENT-SERVICE
server.port=8082

spring.datasource.url=jdbc:mysql://localhost:3306/payment_db
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

3. Order Entity in Order Service

@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String productName;

    private Double amount;

    private String status;

    public Order() {
    }

    public Order(String productName, Double amount, String status) {
        this.productName = productName;
        this.amount = amount;
        this.status = status;
    }

    // getters and setters
}

4. Payment Entity in Payment Service

@Entity
@Table(name = "payments")
public class Payment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long orderId;

    private Double amount;

    private String paymentStatus;

    public Payment() {
    }

    public Payment(Long orderId, Double amount, String paymentStatus) {
        this.orderId = orderId;
        this.amount = amount;
        this.paymentStatus = paymentStatus;
    }

    // getters and setters
}

5. Order Repository

public interface OrderRepository extends JpaRepository<Order, Long> {
}

6. Payment Repository

public interface PaymentRepository extends JpaRepository<Payment, Long> {
}

7. Payment Request DTO

public class PaymentRequest {

    private Long orderId;
    private Double amount;

    public PaymentRequest() {
    }

    public PaymentRequest(Long orderId, Double amount) {
        this.orderId = orderId;
        this.amount = amount;
    }

    // getters and setters
}

8. Payment Response DTO

public class PaymentResponse {

    private Long paymentId;
    private Long orderId;
    private String paymentStatus;

    public PaymentResponse() {
    }

    public PaymentResponse(Long paymentId, Long orderId, String paymentStatus) {
        this.paymentId = paymentId;
        this.orderId = orderId;
        this.paymentStatus = paymentStatus;
    }

    // getters and setters
}

9. Payment Controller in Payment Service

@RestController
@RequestMapping("/payments")
public class PaymentController {

    private final PaymentRepository paymentRepository;

    public PaymentController(PaymentRepository paymentRepository) {
        this.paymentRepository = paymentRepository;
    }

    @PostMapping
    public PaymentResponse makePayment(@RequestBody PaymentRequest request) {

        Payment payment = new Payment(
                request.getOrderId(),
                request.getAmount(),
                "SUCCESS"
        );

        Payment savedPayment = paymentRepository.save(payment);

        return new PaymentResponse(
                savedPayment.getId(),
                savedPayment.getOrderId(),
                savedPayment.getPaymentStatus()
        );
    }
}

10. Enable Feign Client in Order Service

@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

11. Payment Feign Client in Order Service

@FeignClient(name = "PAYMENT-SERVICE", url = "http://localhost:8082")
public interface PaymentClient {

    @PostMapping("/payments")
    PaymentResponse makePayment(@RequestBody PaymentRequest request);
}

12. Order Service Logic

@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final PaymentClient paymentClient;

    public OrderService(OrderRepository orderRepository,
                        PaymentClient paymentClient) {
        this.orderRepository = orderRepository;
        this.paymentClient = paymentClient;
    }

    public Order placeOrder(String productName, Double amount) {

        Order order = new Order(productName, amount, "CREATED");

        Order savedOrder = orderRepository.save(order);

        PaymentRequest paymentRequest = new PaymentRequest(
                savedOrder.getId(),
                savedOrder.getAmount()
        );

        PaymentResponse paymentResponse =
                paymentClient.makePayment(paymentRequest);

        if ("SUCCESS".equals(paymentResponse.getPaymentStatus())) {
            savedOrder.setStatus("PAYMENT_SUCCESS");
        } else {
            savedOrder.setStatus("PAYMENT_FAILED");
        }

        return orderRepository.save(savedOrder);
    }
}

13. Order Controller

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping
    public Order placeOrder(@RequestBody OrderRequest request) {

        return orderService.placeOrder(
                request.getProductName(),
                request.getAmount()
        );
    }
}

14. Order Request DTO

public class OrderRequest {

    private String productName;
    private Double amount;

    public OrderRequest() {
    }

    public OrderRequest(String productName, Double amount) {
        this.productName = productName;
        this.amount = amount;
    }

    // getters and setters
}

15. API Testing

POST http://localhost:8081/orders

{
    "productName": "Java Course",
    "amount": 499.0
}

16. What Happens Internally?

  1. Client calls Order Service.
  2. Order Service saves order data in order_db.
  3. Order Service calls Payment Service using Feign Client.
  4. Payment Service saves payment data in payment_db.
  5. Payment Service returns payment status.
  6. Order Service updates order status.

17. Important Point

Order Service never directly connects to payment_db. Payment Service never directly connects to order_db.

This is the correct implementation of Database Per Service Pattern.

Why this Microservices question is important?

This interview question helps candidates understand real-time backend development concepts, practical problem solving, coding fundamentals, system design basics and production-ready application behavior.

Practice this question carefully for Java backend roles, Spring Boot developer interviews, microservices interviews, company interviews and full-stack developer preparation.

About the Author

Naresh Kumar is a Senior Java Backend Engineer with experience building enterprise applications using Java, Spring Boot, Microservices, Docker, Kubernetes and Cloud technologies.