Database-per-Service Pattern and Data Ownership in Microservices

How I separate data ownership across microservices without forcing one physical database server per service, and how I handle reporting, read projections and migration from shared schemas.

Romharshan Singh
Romharshan SinghSenior Solution Architect • AI & Cloud Mentor
1 September 20263 min read0 viewsUpdated 1 Sept 2026
Database-per-Service Pattern and Data Ownership in Microservices

Database-per-Service Pattern and Data Ownership in Microservices

The principle I care about

The most important part of database-per-service is not:

every microservice must buy its own database server.

It is:

one service owns writes to its domain data.

Bad coupling:

plaintext
Booking Service ----\
Payment Service -----+--> shared tables
Hotel Service -------/

Any service can change another service's data without its business rules.

Better:

plaintext
Booking Service -> Booking data
Payment Service -> Payment data
Vendor Service  -> Vendor data

Physical isolation options

Ownership can be implemented as:

plaintext
separate schema
separate database
separate cluster
separate cloud account/project

The correct level depends on scale, security and operations.

A small platform can keep multiple owned schemas in one managed DB cluster while application permissions prevent cross-domain writes.

Cross-service reads

Option 1: API

http
GET /customers/C1001

Option 2: local projection

plaintext
CustomerUpdated
     |
Kafka
     |
Booking service
     |
customer_snapshot table

Projection:

typescript
@EventsHandler(CustomerUpdated)
export class CustomerSnapshotProjection {
  async handle(
    event: CustomerUpdated,
  ) {
    await this.bookingDb
      .customerSnapshot
      .upsert({
        customerId: event.customerId,
        displayName: event.displayName,
        version: event.version,
      });
  }
}

Reporting

A report that needs:

plaintext
booking + customer + payment + product

should not automatically execute a runtime SQL join across four service databases.

I create:

  • reporting warehouse;
  • analytical store;
  • read projection;
  • event-fed reporting DB.

This keeps transactional ownership clean.

Cross-service transaction

If booking and payment must commit atomically in one database transaction, I ask:

are these actually separate service boundaries?

Sometimes the correct answer is to keep the operation inside one aggregate/service.

When boundaries are valid but operations span them, I use Saga/outbox/idempotency rather than shared writes.

Migration from shared DB

During Strangler migration:

plaintext
Phase 1: shared DB, legacy write owner
Phase 2: new service reads through ACL
Phase 3: new service becomes write owner
Phase 4: legacy receives API/event projection
Phase 5: old table access removed

I avoid uncontrolled bidirectional dual writes.

Production checklist

  • Name the authoritative data owner.
  • Restrict cross-service write credentials.
  • Design APIs/events for other services.
  • Build reporting separately.
  • Plan backup/recovery per domain.
  • Version data contracts.
  • Avoid runtime cross-service SQL.
  • Revisit boundaries when ACID needs cross them constantly.
  • Define migration ownership stage by stage.

FAQ

Does every service need a separate DB server?

No.

Is a shared DB always wrong?

It can be a pragmatic transition, but shared writable tables reduce independent evolution.

How do I perform joins?

For operational reads use APIs/composition/local projections. For analytics use a reporting platform.

- [Microservices Design Patterns I Have Used in Real Enterprise Projects](/articles/microservices-design-patterns-real-enterprise-projects) - [CQRS](/articles/cqrs-command-query-responsibility-segregation-real-systems)
Was this article useful?

Your feedback helps prioritize deeper technical content.

Romharshan Singh
ABOUT THE AUTHOR

Romharshan Singh

Senior Solution Architect and Full Stack Technology Leader with 20+ years of enterprise engineering experience across AI, cloud, distributed systems, Java, Node.js, React, Angular, Kafka and Kubernetes.