Database per Service
A microservices pattern where each service owns its private database. No other service can access it directly. Enforces loose coupling but complicates cross-service queries.
What is Database per Service?
In short
Database per service is a microservices pattern where every service owns its own private database, and no other service is allowed to read or write that database directly. Services share data only by calling each other's APIs or by publishing events, which keeps each service loosely coupled and independently deployable at the cost of harder cross-service queries and transactions.
What it actually means
In a database per service setup, the boundary of a service includes its data. The Orders service has its own orders database. The Payments service has its own payments database. The Inventory service has its own inventory database. Each one is the only piece of code with the connection string, credentials, and schema knowledge for its store.
The rule that makes this a pattern and not just a deployment detail is simple: no service queries another service's tables. If the Orders service needs a customer's email, it does not run a SQL join against the Customers database. It calls the Customers service over HTTP or gRPC, or it consumes a customer-updated event that the Customers service published.
The databases do not even have to be the same engine. Orders might use PostgreSQL, the product catalog might use MongoDB, a session store might use Redis, and a recommendations service might use a graph database like Neo4j. Each team picks the store that fits its access pattern, which is often called polyglot persistence.
How it works under the hood
Physically there are a few ways to enforce the boundary, ordered from loosest to strictest. The cheapest is a private schema per service inside one shared database server. Stricter is a separate database (logical instance) per service on shared hardware. Strictest is a fully separate database server per service. Many teams start with private schemas and split to separate servers only when scaling or blast-radius isolation demands it.
Because there is no shared database, you lose the easy ACID transaction that used to span tables. A checkout that decrements inventory and creates an order now touches two services and two databases. To keep them consistent you use the Saga pattern: a sequence of local transactions where each step publishes an event, and if a later step fails, earlier steps run compensating transactions to undo their work.
Reads that span services are solved with two main tools. The API composition pattern has a caller fan out to several services and stitch the results together in memory. The CQRS pattern maintains a separate read-only view database that is kept up to date by subscribing to events from the owning services, so a query against many entities hits one denormalized table instead of many services.
When to use it and the trade-offs
Use it when you genuinely have independent teams owning independent services and you want them to deploy, scale, and change their schema without coordinating with everyone else. The biggest win is exactly that: a team can alter its tables, add an index, or migrate engines without breaking another team, because nobody else is allowed to touch its data.
The cost is real. You give up cross-table joins and single-database transactions. You take on eventual consistency, duplicate data across services, and the operational burden of running many databases with their own backups, migrations, and monitoring. Reporting and analytics get harder because the data lives in many places, which usually means building a separate data warehouse fed by change-data-capture.
If your application is small, has one team, or relies heavily on transactions and joins across the whole dataset, a single shared database (the monolith approach, or a modular monolith) is usually the right call. Database per service pays off at organizational scale, not at the start of a project. Splitting too early is one of the most common and expensive microservices mistakes.
A concrete example
Picture an e-commerce checkout. The Order service writes a new row to its orders database with status PENDING and publishes an OrderCreated event. The Payment service consumes that event, charges the card against its own payments database, and publishes PaymentCompleted. The Inventory service consumes PaymentCompleted, decrements stock in its inventory database, and publishes InventoryReserved. The Order service consumes that and flips the order to CONFIRMED.
If the card is declined, the Payment service publishes PaymentFailed instead. The Order service consumes it and marks the order CANCELLED, and any inventory that was tentatively held is released by a compensating action. No single transaction spans all four databases; consistency is reached through this chain of events, typically within a few hundred milliseconds.
Amazon ran this transition publicly. Its early monolith shared one database; over years it broke into hundreds of services, each owning its data, which is what let thousands of engineers ship independently many times a day.
Where it is used in production
Amazon
Decomposed its monolithic retail platform into services that each own their data, enabling thousands of independent deployments per day.
Netflix
Hundreds of microservices each manage their own datastore (often Cassandra), avoiding a single shared database across the streaming platform.
Uber
Splits domains like trips, payments, and driver state into separate services with isolated schemas, using events to keep them in sync.
Shopify
Uses service-owned databases (largely MySQL with sharding) so domain teams can scale and migrate storage without coordinating across the whole app.
Frequently asked questions
- Can two services share the same database server?
- Yes, sharing a server is fine as long as each service uses its own private schema or logical database and never reads another service's tables. The pattern is about data ownership and access rules, not necessarily separate hardware. Many teams start with one server and split to separate servers later for scaling or isolation.
- How do you do a transaction across two services?
- You cannot use one ACID transaction across two databases, so you use the Saga pattern instead. A saga is a sequence of local transactions, each publishing an event; if a step fails, earlier steps run compensating transactions to undo their effects. This gives eventual consistency rather than immediate consistency.
- How do you run a query that needs data from several services?
- Two common options. API composition has a caller fetch from each service and join the results in code. CQRS maintains a separate denormalized read model that subscribes to events from the owning services, so the query hits one prebuilt view instead of many services. Use CQRS when the query is hot or the fan-out is large.
- Is database per service the same as polyglot persistence?
- They are related but not the same. Database per service means each service owns its data privately. Polyglot persistence means different services can use different database engines (PostgreSQL, MongoDB, Redis, and so on). Database per service makes polyglot persistence possible because each service is free to pick its own engine, but you can also have database per service where every service happens to use the same engine.
- When should I not use this pattern?
- Skip it for small apps, single-team projects, or systems that depend heavily on cross-entity joins and strong transactions. Splitting databases too early adds eventual consistency, data duplication, and operational overhead before you have the team size or scale to justify it. A modular monolith with one database is usually the better starting point.
Learn Database per Service hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
See also
Related glossary terms you might want to look up next.
Microservices
An architecture where an application is split into small, independent services that communicate over the network. Each service owns its own data and can be deployed separately.
Saga Pattern
A way to manage distributed transactions across microservices using a sequence of local transactions with compensating actions for rollback.
Data Federation
Splitting databases by function (users in one DB, orders in another) so each database handles only its domain. Simpler than sharding but limits cross-domain joins.
Monolith
A single, unified application where all features share the same codebase and deployment. Simpler to start with but harder to scale individual parts.
Service Discovery
The mechanism by which microservices find and communicate with each other. Services register themselves and others can look them up by name.
Circuit Breaker
A pattern that stops calling a failing service after repeated failures, preventing cascade failures. Like an electrical circuit breaker that cuts power to prevent fires.