Overview

The Database per Service pattern defines a private database for each microservice, making the database a component of the service. No other service can access it directly — only through the API the service exposes. This is the foundational data architecture pattern for microservices.

Problem

How should data be persisted in a microservices architecture? The most intuitive approach — a single shared database — contradicts the core principles of microservices:

  • A shared database creates tight coupling between services at the data layer
  • Schema changes in one service can break others
  • Services cannot independently choose the best database technology for their needs
  • It becomes impossible to scale individual services' databases independently

Solution

Assign a private database to each microservice. Only that service can access its own data store directly. Other services must go through the service's API. This enforces decoupling and enables independent evolution.

Implementation Approaches

There are three ways to implement this pattern, with different trade-offs:

Private Tables per Microservice

Each microservice owns a set of tables within a shared relational database. Access control is enforced at the application level. This approach shares infrastructure but provides logical isolation.

Advantages: Lower resource overhead, simpler infrastructure.
Disadvantages: Harder to enforce boundaries — other services could bypass the API and access tables directly. Cannot use different database technologies per service.

Schema per Microservice

Each microservice gets its own schema within a shared database server. Stronger isolation than private tables, but still shares the underlying database engine.

Advantages: Better logical isolation than private tables, still shared infrastructure.
Disadvantages: Still cannot use different database technologies. Coordination overhead to prevent schema boundary violations.

Database Server per Microservice

Each microservice has its own dedicated database server. This is the most complete implementation of the pattern.

Advantages:

  • Freedom to choose the best technology for each service (SQL, NoSQL, graph, search engine, etc.)
  • No resource sharing — one service's load doesn't affect others
  • Can apply quality attributes independently per service (scalability, availability, replication)
  • True decoupling — changes to one service's database have no impact on others

Disadvantages: More resources needed; more components to maintain, monitor, and deploy.

Trade-offs

Benefits

  • True service independence — no shared data layer
  • Freedom to choose the best database technology per service
  • Independent scalability of each service's data store
  • Schema changes are fully isolated to the owning service

Drawbacks

  • Queries spanning multiple services require additional patterns (API Composition, CQRS)
  • Distributed transactions are no longer possible — requires Saga pattern
  • More infrastructure to provision, monitor, and maintain

When to Use

Apply Database per Service when:

  • Building a microservices architecture — this is the default pattern for data persistence
  • Different services have significantly different data access patterns or scaling requirements
  • You need different database technologies per service (e.g., a search service needs Elasticsearch, an order service needs a relational DB)

Adopting this pattern introduces new challenges that are addressed by companion patterns:

  • Saga — manages transactions that span multiple services
  • CQRS — enables efficient reads across multiple service databases
  • API Composition — aggregates data from multiple services for simple read queries