Data Management in Microservices
Context
Section titled “Context”You have split a monolith into services. Each service now runs as its own deployable, with its own team and its own release cadence. That decomposition feels clean on a whiteboard — until you reach the database. In the monolith there was one schema, one connection pool, one transaction boundary. A single BEGIN ... COMMIT could touch the orders table, the inventory table, and the customer ledger atomically, and a single SELECT with a join could stitch them back together for a report.
The moment those tables live behind separate services, that shared foundation is gone. This module is about what replaces it.
Problem
Section titled “Problem”If every service is truly independent, then no service may reach into another service’s tables. But real business operations rarely respect service boundaries. Placing an order touches orders, inventory, and payments. A customer dashboard needs data scattered across half a dozen services. So the central question of this module is this:
How do you keep data consistent and queryable across services when there is no shared database, no distributed join, and no global transaction?
Solution
Section titled “Solution”The answer is not a single mechanism but a small family of cooperating patterns. The first establishes the boundary; the rest restore the capabilities the boundary takes away.
Each service keeps its own private store. Other services interact with that data only through the owning service’s API or through the events it publishes.
flowchart TB
Client[Client / Gateway] --> OAPI
Client --> IAPI
Client --> PAPI
subgraph Orders[Order Service]
OAPI[API]
ODB[(Orders DB)]
OAPI --> ODB
end
subgraph Inventory[Inventory Service]
IAPI[API]
IDB[(Inventory DB)]
IAPI --> IDB
end
subgraph Payments[Payment Service]
PAPI[API]
PDB[(Payments DB)]
PAPI --> PDB
end
OAPI -. events .-> IAPI
OAPI -. events .-> PAPI The five patterns in this module fit together like this:
- Database per Service — every service owns a private database that only it may touch directly. This is the foundation that makes the rest necessary.
- Saga — keep data consistent across services without a distributed transaction, by chaining local transactions together and undoing them with compensating actions when something fails.
- API Composition — answer a query that spans services by calling each one and joining the results in memory.
- CQRS — separate the write side from one or more purpose-built read models kept in sync by events, so cross-service queries become fast local lookups.
- Event Sourcing — store state as an append-only log of events, replay the log to reconstruct state, and reliably publish those same events to everyone else.
What this module covers
Section titled “What this module covers”We start with the boundary (Database per Service), then tackle writes that span services (Saga) and reads that span services (API Composition and CQRS), and finish with a storage model that makes reliable event publishing natural (Event Sourcing). Each lesson follows the same shape: the situation that leads to the pattern, the forces in tension, what the pattern does, a diagram, runnable example code, and an honest look at what the pattern costs you.
Related patterns
Section titled “Related patterns”- Database per Service — start here, it is the foundation.
- Saga — the showcase pattern for cross-service writes.