Database Per Service
matureSummary
Assign each service exclusive ownership of its own database schema or instance, prohibiting direct cross-service database access and enforcing data encapsulation at the service boundary.
Problem
Shared databases create coupling between services: schema changes cascade across service boundaries, slow queries from one service degrade others, and independent deployment and scaling become impossible when services share a data store.
Description
In a shared-database architecture, multiple services read and write the same tables. Schema changes in one service break others. A slow query from one service degrades database performance for all services. Operational incidents in one service can corrupt data owned by another. Data model coupling prevents services from being deployed or scaled independently.
Database-per-service enforces a hard boundary: each service owns a set of tables (or an entire schema or database instance) that no other service can access via SQL. The only way to read or modify another service's data is via that service's API. This enables independent schema evolution, independent scaling, independent operational failure domains, and independent technology selection (one service uses PostgreSQL, another uses Redis, another uses Cassandra).
The boundary is enforced at multiple levels. At the network level: database credentials are service-specific; no service has credentials for another service's database. At the schema level: tables are prefixed or namespaced per service; global database users do not have cross-service grants. At the process level: shared ORM models or shared schema migration tools are a code smell that signals boundary violation.
The fundamental operational consequence is that queries requiring data from multiple services cannot be expressed as SQL JOINs. They require one of: (1) API composition : the calling service fetches from each service's API and joins in memory; (2) event-driven denormalisation: downstream services subscribe to upstream events and maintain their own read-optimised copies of relevant data; (3) a dedicated reporting store that aggregates data from multiple services for analytical queries.
Cross-service ACID transactions are impossible without a distributed transaction protocol. The Saga pattern is the standard substitute, accepting eventual consistency at the business transaction level.
Tradeoffs
Complete schema, deployment, and scaling independence per service
Database failure in one service does not affect other services
SQL JOINs are impossible; API composition or event denormalisation required
Cross-service ACID is impossible without 2PC; Saga provides eventual consistency only
Multiple databases to provision, monitor, backup, and operate
Eventual consistency via events; stale reads during propagation lag
When to use
Services are independently deployable and owned by separate teams
Independent deployment requires independent data schemas; a shared database forces coordinated deployments whenever a schema change is made
Services have different scaling requirements
A write-heavy service and a read-heavy service have different database scaling strategies; database-per-service allows each to scale independently
Service boundaries are well-defined with stable API contracts
Data ownership only works when it is clear which service owns which data; ill-defined boundaries result in ownership disputes and cross-service queries
Team can maintain Saga patterns or API composition for cross-service data
Eliminating cross-service SQL joins requires alternative integration patterns; team must be capable of implementing and operating these
When not to use
Services are small and owned by a single team without operational independence needs
For small systems, a single database with schema namespacing achieves most of the benefit with a fraction of the operational overhead
Reports and analytics require frequent ad-hoc joins across many service domains
Analytical queries joining across many service databases require either an integrated reporting database or an ETL pipeline; if this is the primary use case, sharing a database for analytics is pragmatic
Strong ACID transactions across service boundaries are required
Database-per-service makes cross-service ACID transactions impossible without 2PC; if the business domain requires them, the service boundaries are wrong
Operational Requirements
Enforce database credential isolation: no service holds credentials for another service's DB
Without credential isolation, the boundary is advisory only; direct cross-service SQL queries will be written under deadline pressure
Define data ownership boundaries in documentation before implementation
Unclear ownership produces ownership disputes and cross-boundary queries; define which service owns each domain entity before coding
Design and deploy an integrated reporting solution for cross-domain analytics
Ad-hoc analytics across many service databases requires a separate reporting store or data warehouse; plan this before the first cross-domain report is requested in production
Version event schemas from day one for cross-service event integration
Services subscribed to each other's events will break if event schemas change without versioning; schema evolution must be planned upfront
Characteristics
Technologies
Canonical
Alternatives
Relationships
Evolves from
Complements
Conflicts with
Basis
Core microservices pattern with well-documented operational consequences; cross-service query and transaction challenges are the primary adoption blockers
Related Architecture Knowledge
Inbound: affects this entity
Event sourcing and database-per-service reinforce each other: each service owns its event log and materializes its own read models independently, with cross-service data sharing happening via published events rather than shared database access.
Tradeoffs
- ·Cross-service queries require eventual consistency: no joins across service event logs
- ·Event schema versioning is a distributed coordination problem: schema changes require coordinated deployment
- ·Debugging cross-service workflows requires distributed tracing (correlation IDs across event boundaries)
Strangler Fig migration progressively extracts services from a monolith; each extracted service is the natural point at which a dedicated database is introduced, evolving the shared monolith database toward a database-per-service topology as the migration progresses.
Tradeoffs
- ·Dual-write migration window is operationally complex: must reconcile divergence before final cutover
- ·Database extraction is the hardest part of strangler fig: shared tables with join dependencies require careful ordering
- ·Each extracted service needs its own connection pool, backup, and monitoring: operational complexity grows linearly