Give each microservice exclusive ownership of its own data store, so no service can be broken by another team's schema change.
Splitting a monolith into services but leaving them all reading and writing one shared database doesn't actually decouple them — any service can still be broken by another team changing a column, and nobody can deploy a schema migration without coordinating with every other service that touches that table.
Every service has its own database, accessible only to that service. Other services never query it directly — if they need that data, they call the owning service's API. The owning service is free to change its internal schema at will, as long as its API contract stays stable.
`billingService` never opens a connection to the orders database — it asks `ordersService`'s API for what it needs.
// Orders service owns this data exclusively.
const ordersDB = new Map([["order-1", { id: "order-1", total: 42 }]]);
const ordersService = {
getOrder: (id) => ordersDB.get(id), // the only way anyone reaches this data
};
// Billing service has its own, separate database.
const billingDB = new Map();
const billingService = {
createInvoice(orderId) {
const order = ordersService.getOrder(orderId); // via API, never a direct DB query
if (!order) throw new Error("order not found");
billingDB.set(orderId, { orderId, amount: order.total });
console.log(`billing: invoice created for ${orderId}, amount ${order.total}`);
},
};
billingService.createInvoice("order-1");
// billingService has no idea what ordersDB even looks like internally.