The Shared Database Is the Real Monolith
Teams routinely extract services while leaving every one of them pointed at the same relational schema. The deployment topology looks distributed; the coupling is unchanged. A schema migration still requires coordinating every service, a slow query in one service still starves the others, and no service can be reasoned about in isolation. The monolith simply moved down a layer.
Genuine decomposition means each service owns its data exclusively. No other service reads its tables directly. That constraint is what makes independent deployment, independent scaling, and independent failure domains possible — and it is the constraint most migrations quietly abandon.
Sequencing the Split
The workable order is: identify bounded contexts, break foreign keys that cross them, replace cross-context joins with API calls or replicated read models, and only then physically separate the databases. Attempting physical separation first produces a period where the system is neither consistent nor operable.
- Use the strangler-fig pattern: route new writes to the new service while the old path still reads.
- Replace cross-context foreign keys with soft references and validation at the service boundary.
- Materialize read models for queries that previously relied on joins across contexts.
- Run dual-write with reconciliation before cutting over, then delete the legacy path deliberately.
Transactions Across Boundaries
Once data is segregated, the ACID transaction that spanned two tables no longer exists. The replacement is the saga: a sequence of local transactions, each with a compensating action if a later step fails. Orchestrated sagas centralize the workflow in a coordinator and are easier to debug; choreographed sagas propagate events and couple services less tightly but make the overall flow harder to observe.
The transactional outbox pattern solves the dual-write problem — writing the state change and the outgoing event in one local transaction, with a relay publishing from the outbox. Without it, services routinely commit state without emitting the event, and the system drifts silently.
Reporting After the Split
The reporting queries that joined fifteen tables are the migration's hidden dependency. The answer is not to preserve shared access for the analytics team. It is change data capture into a warehouse or lakehouse, where cross-context joins are legitimate because the warehouse is a separate consumer, not a coupled participant. Budget for this work explicitly; migrations stall when reporting is discovered late.
