The Database-per-Service Problem
Understand why splitting a monolith's single database into one private database per microservice is a deliberate trade-off, and get familiar with the four-service bank example used as a running case study.
Learning objectives
- Explain why microservices are typically given their own private database instead of sharing one.
- Describe the four services used as a running example throughout this topic.
- List the concrete benefits that motivate database-per-service.
- Identify the new problems this decision creates for cross-service data access.
◆ Story
Picture a bank that runs its entire back office out of a single shared filing room. Customer records, savings accounts, credit cards, and loan paperwork all sit in the same cabinets, and any clerk from any department can walk in and pull any file whenever they need it. It feels convenient, right up until it doesn't: if the loans team decides to reorganize their folders into a new structure, every other clerk who was quietly relying on the old layout suddenly can't find what they're looking for. Nobody in loans even knows they broke something for someone else, because nothing stopped them from changing a shared resource that other departments depended on.
Now picture the same bank splitting into four separate departments, each locked inside its own filing room that only that department can open. The loans team can now reorganize their cabinets however they like, whenever they like — nobody else is affected, because nobody else was ever allowed to reach directly into loans' cabinet in the first place. Deployment day for loans no longer requires a phone call to three other teams.
But there's a catch. If a branch manager wants one combined summary of a customer — their profile, their accounts, their cards, and their loans, all on one page — nobody can walk into a single room and grab a single file anymore. Someone now has to visit all four rooms, one after another, and manually combine what they find.
That is the entire trade-off at the heart of this topic: real independence between teams and systems, purchased at the cost of losing effortless, one-stop access to combined data. In software terms, this is what people mean when they talk about giving each microservice its own private database — sometimes called the database-per-service pattern — and learning to handle that trade-off well, instead of fighting it, is the foundation for almost everything that follows.
We'll use one consistent example throughout: the backend of a small bank, built as four independent microservices, each with a clear, narrow area of responsibility.
| Service | What it owns |
|---|---|
| Customer Service | A customer's basic profile — name, email, contact details. |
| Accounts Service | A customer's savings or checking accounts and their balances. |
| Cards Service | A customer's credit cards. |
| Loans Service | Any loans a customer has taken out. |
Each of these four services owns its own private database, and none of them is allowed to reach directly into another service's tables. This isn't a toy simplification for teaching purposes — it's a genuinely common, realistic shape for a real banking backend, and it's exactly the shape that makes every pattern covered later necessary rather than merely theoretical.
Notice what "owns" means here: only the Accounts service is allowed to write to the accounts database, and in a strict version of this design, only the Accounts service is even allowed to read from it directly. Any other service that wants to know something about a customer's balance has to go through the Accounts service's API, not its database. This is the rule that makes the whole design work, and it's also exactly the rule that creates the problems the rest of this topic exists to solve.
This split also mirrors how many real banks and fintech companies actually separate concerns internally: customer identity data behaves very differently from a ledger of account transactions, which in turn is regulated differently from card issuance or loan underwriting. Modeling them as separate services isn't just a technical convenience — it often lines up with real organizational and compliance boundaries too. We'll build small, concrete pieces of Java and Spring code against this same four-service bank as we go.
Before looking at what breaks, it's worth being honest about why teams choose this split on purpose. It isn't an accident, and it isn't an oversight by someone who forgot to add a shared connection string — it's a deliberate trade for four real benefits.
| Benefit | In plain words |
|---|---|
| Independent deployment | The loans team can change their database schema and deploy on a Tuesday afternoon without asking the cards team for permission, or even telling them. |
| Independent scaling | If accounts traffic grows ten times larger than loans traffic, only the accounts database needs to scale up — the loans database stays exactly as it was. |
| Fault isolation | If the loans database has an outage, customer, accounts, and cards keep working completely normally. |
| Technology freedom | Cards could use a relational database while loans uses a document store or a different engine entirely, if that genuinely fits its data better. |
These four benefits are the entire reason database-per-service exists as a recommended default in modern service design. A team that shares one database across several services quietly gives every one of these benefits back — even if nobody explicitly decided to. Schema changes become coordinated, cross-team events; scaling one workload forces scaling all of them together; and an outage in one part of the shared database can take down services that have nothing to do with the original problem.
◆ Under the hood
"Independent deployment" is often the benefit teams notice first, but "fault isolation" is usually the one that saves them in production. A connection pool exhausted by a runaway query in one service's database, when that database is shared, can starve every other service that happens to use the same instance — even ones that were behaving perfectly.
◆ The problem
Every benefit in the previous section is real. But splitting the filing room, exactly as the opening story showed, genuinely breaks things that used to be trivially easy — and pretending otherwise is exactly how teams get blindsided once their system is live in production.
| Challenge | In plain words |
|---|---|
| Cross-service queries | Showing a customer's full profile — data spread across all four services — on one screen. |
| Data consistency across services | Making sure one business action that touches multiple services either fully succeeds everywhere or fully fails everywhere, with no in-between state. |
| Data duplication | Two services each keeping their own copy of the same fact, and those copies slowly drifting apart from each other over time. |
These three challenges aren't edge cases a team might run into occasionally — they're the default, everyday consequence of splitting a database. Every team that adopts database-per-service runs into all three, usually within the first few months of building on top of it. Each one is worked through in turn elsewhere, starting with the simplest: how to answer a read that spans multiple services at all.
▲ Common mistake
A tempting "fix" for these problems is to quietly let one service reach directly into another service's tables — for example, letting Accounts read straight from Customer's database to avoid an API call. This throws away almost every benefit described above: the schema is no longer private, scaling is coupled again, and an outage in one database can now take down a service that never even had its own database problem. Reaching into another service's database isn't a real solution — it's giving up on the very independence that motivated splitting the databases in the first place.
Q: Why does each microservice typically get its own private database instead of sharing one? A: So that each service can be deployed, scaled, and kept fault-isolated independently of every other service — none of that is possible if they all depend on one shared database.
Q: What are the four services in the running bank example used throughout this topic? A: Customer Service, Accounts Service, Cards Service, and Loans Service — each owning a distinct, narrow slice of the bank's data.
Q: What are the three main problems that database-per-service creates? A: Cross-service queries (combining data from multiple services for one screen), data consistency across services (making a multi-service action atomic), and data duplication (copies of the same fact drifting out of sync).
Q: Why is letting one service read directly from another service's database not a real fix? A: Because it quietly undoes the independent deployment, scaling, and fault isolation benefits that motivated splitting the databases in the first place.
Want a visual for this concept?
Generate a diagram tailored to “The Database-per-Service Problem” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →