Monolith migration data ownership should move only after a capability boundary and its invariants are understood. Extracting code and creating a new database in the same step feels decisive, but it introduces remote calls, distributed failure, data synchronization, and reconciliation before the team has proved what the new service actually owns. A safer migration separates logical ownership from physical storage and transfers write authority through observable stages.
The goal is not to preserve a shared database forever. It is to avoid two accidental systems of record. At every stage, name the authoritative writer for each business fact, define which copies are projections, and specify how stale or failed synchronization is detected and repaired. Readers and reports can move at a different pace from commands when their consistency requirements allow it.
Establish the current data and invariant map
Inventory entry points, business operations, tables, aggregates, stored procedures, scheduled jobs, exports, direct integrations, and support scripts. For each fact, record writers, readers, atomic invariants, retention duties, personal-data constraints, volume, and recovery requirements. Query logs and code search often reveal writers that architecture diagrams omit. Include manual fixes performed by operations because they are part of the real ownership model.
Define the candidate capability in business terms and identify the transactions that make it true. An order acceptance may need inventory reservation, payment authorization reference, price snapshot, and status transition. Some outcomes can converge later; others cannot. AWS’s strangler fig guidance warns that premature decomposition is costly when domain boundaries are unclear and describes domain-driven analysis before extraction.
Choose a migration stage for each data set
| Stage | Authoritative writer | Read path | Primary risk |
|---|---|---|---|
| Baseline | Monolith | Monolith database | Unknown hidden writers and invariants |
| Encapsulated | Monolith module behind an internal API | Existing tables through owner module | Bypass paths remain |
| Extracted code, shared store | New service or deployable module | Shared database with restricted ownership | Deployment coupling and schema contention |
| New store with projection | One chosen writer only | Owner store plus read-only replicated projection | Lag, ordering, and replay defects |
| Consumer cutover | New service | New API or governed projection | Long-tail reports and scripts |
| Final ownership | New service | New store; legacy copy retired | Incomplete archive, rollback, or reconciliation |
Do not force every table through the same stage. Reference data can remain centrally governed; high-volume read models may become projections early; a core ledger may stay in the monolith until controls are ready. Maintain a versioned register so architecture, application, data, and operations teams agree on current authority.
Encapsulate behavior before extracting storage
Create an internal module interface around the candidate capability while it still runs in the monolith. Route writes through one application service, reject new direct table access, and add characterization tests for invariants and error behavior. This step produces leverage even if network extraction is postponed. It also reveals whether the supposed boundary depends on ambient transactions, shared caches, session state, or undocumented callbacks.
When legacy callers use a model that should not leak into the new capability, add an anti-corruption layer. Microsoft’s Anti-Corruption Layer pattern describes a facade or adapter translating between systems with different semantics. Keep the adapter at the boundary, test both directions, measure its use, and give it a retirement condition; otherwise transitional translation becomes permanent shared language.
Transfer write authority exactly once
Select a single-writer cutover strategy. One option routes a cohort of commands to the new service while the monolith becomes a client. Another moves all commands at once after shadow validation. Avoid independent dual writes from application code: a crash between commits leaves ambiguous authority. If an atomic database commit must publish a change, use a transactional outbox so the business update and message record commit together, then relay idempotently.
AWS’s transactional outbox pattern addresses the dual-write problem while emphasizing duplicate delivery, ordering, and consumer idempotency. The outbox does not guarantee that downstream projections are current. Every event needs a stable identifier, aggregate identity, sequence or version, schema version, event time, and trace context, plus a replay policy.
Design reconciliation and rollback before cutover
| Control | Before cutover | During transition | Exit criterion |
|---|---|---|---|
| Writer enforcement | Database permissions and code scan identify all writers | Alert on denied legacy writes | No unauthorized write attempts for agreed window |
| Projection quality | Backfill totals and key samples reconcile | Lag, failures, duplicates, and version gaps monitored | SLA met under peak and replay |
| Business reconciliation | Define invariant-level comparisons | Compare counts, amounts, statuses, and exceptions | Differences explained or repaired |
| Rollback | Define routing reversal and data-forward plan | Preserve command and event identity | Drill completes within recovery objective |
| Consumer inventory | Register reports, jobs, APIs, and owners | Track old-path usage | Every consumer migrated, retired, or explicitly retained |
| Legacy retirement | Define archive and retention duties | Block new dependencies | Old writer and redundant copy removed |
Reconcile business meaning, not just row counts. Compare totals by currency and date, legal status transitions, uniqueness constraints, and orphan relationships. Store mismatch cases with reason and disposition. For mutable records, compare versions at a common watermark so normal replication lag does not appear as corruption. Run replay and recovery drills with production-scale shapes before making the old writer unavailable.
Rollback after write transfer is usually roll-forward with routing reversal, not restoration of an old snapshot. Commands accepted by the new owner must be preserved and applied to whichever system resumes authority. Document the point after which physical rollback is unsafe and require incident command approval for crossing it.
Retire legacy reads, copies, and migration machinery
Move consumers according to their freshness and availability needs. Interactive commands should call the owner. Search, analytics, and batch reports can use governed projections. Track queries against legacy tables, contact owners, and set a final date. Google Cloud’s microservices architecture overview emphasizes independent services and decentralized data management, but independence is achieved only when old shared access is actually removed.
After the exit criteria hold, revoke old write permissions, remove synchronization in the obsolete direction, archive data according to policy, and delete adapters, flags, topics, and dashboards created solely for migration. Keep the decision record, reconciliation results, and recovery evidence. A migration is incomplete while two stores can plausibly be mistaken for authority.
Schema evolution needs two compatible directions during transition. Add nullable or defaulted fields before new writers require them, deploy readers that tolerate both forms, backfill with checkpoints, then make constraints strict only after old writers disappear. Renaming a column is usually add, copy, switch, and remove rather than an atomic rename across independent deployments. Include rollback-compatible schema in the release plan until the old code can no longer return.
Privacy and retention obligations follow every copy. A projection should contain only fields its consumers need, inherit deletion or correction signals, encrypt and authorize access, and have its own retention owner. During backfill, avoid moving obsolete personal data simply because it exists. Reconciliation logs should reference safe identifiers and protect mismatch payloads, which often contain the very records under migration.
Budget temporary architecture explicitly. Change capture, duplicate storage, egress, queue retention, backfill compute, reconciliation jobs, and dual operational dashboards can make the transition more expensive than either endpoint. Track these costs with planned retirement dates. A migration path with no funded cleanup tends to stabilize at its most complex stage.
Finally, test ownership under failure. Pause the relay, restore the new store from backup, replay across a schema change, deliver duplicates, and make the legacy reader unavailable. Confirm that operators can identify authority from dashboards and runbooks without asking the migration authors. A boundary is operational only when ordinary responders can recover it.
Key takeaways
- Map actual writers, readers, invariants, reports, and support scripts before choosing a service boundary.
- Encapsulate business behavior and logical ownership before introducing a separate store.
- Transfer command authority once; use an outbox and idempotent consumers instead of uncoordinated dual writes.
- Design watermark-aware reconciliation, replay, and roll-forward recovery before cutover.
- Retire legacy access and migration components only after every consumer has an accountable disposition.
Frequently asked questions
Can an extracted service temporarily use the monolith database?
Yes, as a controlled stage. Give the service exclusive write ownership of its tables or records, restrict other access, version schema changes, and define an exit criterion. Shared physical storage preserves operational coupling, so it should not be described as full independence.
Is dual writing ever acceptable?
A migration tool can deliberately write two stores when it has durable state, idempotency, reconciliation, and a named authority. Independent application writes to both stores are unsafe because partial failure creates ambiguity. Prefer one writer plus a durable change publication path.
Should historical data be backfilled before live events start?
Often the safer sequence is establish a change watermark, start capturing new changes durably, backfill history to that watermark, then replay and reconcile later changes. The exact method depends on source capabilities, but it must prevent a gap between snapshot and live capture.
Conclusion
A successful monolith migration makes authority clearer at every intermediate state. Prove the capability boundary, encapsulate writes, move command ownership once, distribute projections with explicit consistency, and reconcile business invariants until legacy access can be removed. Sequencing data ownership this way converts a risky database split into a series of reversible, evidence-backed decisions.