Database per Service Lab
Orders, Customers, Inventory, Billing and Reporting each have a team, and they all use one database. Over a quarter, the teams take turns to change their schemas. Watch how long each change waits for other teams, whose queries break, and what Reporting's month-end query does to everyone. Then give each service its own database and choose how the others get its data: by calling its API, from one shared replica, or from local copies kept up to date by events.
Background: Database per Service, Shared Database, Change Data Capture.
Over 13 weeks, Orders, Customers, Inventory and Billing take turns to ask for a schema change, one every 4 working days. Two in three change a column other teams read. Reporting reads everyone's data and runs a heavy query at each month end. Customers, Inventory and Orders each have one outage of their own in the quarter.
Run these in order
Share one database
Five teams use one database and read each other's tables. Every schema change goes to the Tuesday review with every team that reads the table, and the database takes one migration in its Thursday release window. Changes queue for weeks and five of them are still waiting at the end of the quarter. Some reviews miss a team's query, which breaks when the change ships. At each month end, Reporting's heavy query slows everyone past 500 ms.
Move the reports to a read replica
Reporting now queries a copy of the shared database. The month-end slowdowns disappear. Lead time, review meetings and broken queries stay exactly as they were. Load was the easy part: the coupling is in the schema everyone reads.
Split the database, call each other's APIs
Each service gets its own database, and asks the owner's API for anything else. A schema change is now the owner's business: it ships in under four days, needs no meeting and breaks nobody. But queries that used to be joins are now calls, so the average service is three times slower, and when Customers, Inventory or Orders goes down, every service that calls it goes down too. The coupling has moved from the schema to runtime.
Split the writes, share the reads
Each service writes to its own database, and change data capture copies every table into one shared replica that everyone reads. Queries are fast and nobody is down when another service is. But teams still read each other's tables, so every change still needs a review and the same queries still break. Separate databases with a shared schema are still a shared database.
Keep local copies from events
Each owner publishes change events from an outbox table, read by change data capture, and each service keeps the few fields it needs in its own database. Changes ship alone, no team pages another, and queries stay at 40 ms. The price is copies two seconds behind, nineteen things to run instead of one, and no joins or transactions across services: placing an order is now a saga.
Let the pipeline stall
The broker stops for three hours on day 28. No service goes down, but every copy falls behind, and Orders keeps selling stock that Inventory has already sold. Watch the oversold orders. Now tick “Orders reserves stock by calling Inventory”: overselling stops, and Inventory's outage reaches Orders again. Use copies for showing data, and ask the owner when a decision must be right.
This lab simplifies reality on purpose. Every schema change takes the same effort, every review takes a week, and a missed reader is a coin toss rather than a judgement call. Splitting the database is free and instant here, while in real life moving data out of a shared schema takes months and often uses change data capture to keep both sides in step during the move. The lab also leaves out how much data each copy holds, backups, and the work of rewriting each join. The point is the shape of the results, not the exact days.