Break apart a monolith incrementally by choosing a business capability, introducing a controlled seam and moving behavior and data ownership through tested stages. The Strangler Fig pattern manages routing to replacement capabilities, Branch by Abstraction manages implementation substitution behind a code contract, and change data capture can help synchronize data. They solve different migration problems and can be combined.
Key takeaways
- Define the reason for extraction before selecting the mechanism.
- Route reversal is useful only while data and contracts remain compatible.
- CDC exposes data changes; it does not automatically provide business-event meaning or write ownership.
- Retire transitional components after the replacement boundary is verified.
Strangler Fig, Branch by Abstraction and CDC
Microsoft’s Strangler Fig guidance describes gradually replacing functionality through a facade that routes between old and new systems. Martin Fowler’s Branch by Abstraction explanation uses an abstraction so old and new implementations can coexist during a gradual change. SQL Server CDC documentation describes capturing table modifications for downstream consumption.
| Mechanism | Useful seam | What it does not solve automatically |
|---|---|---|
| Strangler Fig | Request or capability routing | Data cutover, duplicate actions and replacement correctness |
| Branch by Abstraction | Application-level implementation contract | Remote-call failure semantics or schema migration |
| CDC | Changes from an authoritative data source | Business semantics, reconciliation or competing writers |
Choose the seam the existing system actually exposes. A well-defined route can support request redirection; an internal library dependency may be easier to replace behind a contract. A database-centric system may need a data integration stage before behavior can move.
Do not extract every capability by default. A supported modular monolith may satisfy the business objective with less operational work. State whether the migration aims to improve release independence, isolate a scaling hotspot, replace unsupported technology or clarify ownership.
A migration inventory for one bounded capability
| Inventory item | Record | Why it matters |
|---|---|---|
| Entry points | UI routes, APIs, jobs and internal calls | Hidden callers can keep old behavior active |
| Business rules | Authoritative validations and state transitions | Replacement must preserve intended semantics |
| Data | Tables, files, owners and write paths | Cutover requires a clear source of truth |
| Integrations | Messages and external actions | Shadow or replay can repeat real actions |
| Identity | User, workload and support access | New paths must enforce the same business boundary |
| Operations | Objectives, monitors, recovery and owners | The replacement must be operable |
| Consumers | Versions and contract expectations | Old and new clients may coexist |
Assign owners to unresolved entries. A table that “everyone uses” or a scheduled task with no maintainer is a migration risk. Discover it before choosing a cutover date.
Capture representative successful and rejected operations as characterization tests. Preserve correct behavior deliberately while identifying legacy defects that should change through explicit product decisions.
A staged migration map
1. Inventory capability and contracts
2. Introduce route or code abstraction over current behavior
3. Build replacement behind the seam
4. Synchronize required data and reconcile
5. Validate replacement with safe test or shadow reads
6. Shift a bounded cohort; preserve one write authority
7. Complete cutover and observe
8. Retire legacy behavior and transitional plumbingStage 1: make the current path explicit
Put the existing implementation behind the chosen seam without intentionally changing its behavior. Verify routing, identity propagation and observability. This separates seam defects from replacement defects.
Stage 2: build and compare the replacement
Use the same business contracts and authorized test data. Shadow reads can compare outputs when they are safe, but shadow writes can create duplicate external effects. Keep comparison traffic isolated from real actions unless a reviewed mechanism explicitly prevents them.
Stage 3: move a controlled cohort
Assign tenants, operations or partitions deliberately. Preserve a stable cohort if sessions or related operations must stay together. Monitor correctness, latency, errors and reconciliation rather than traffic percentage alone.
Stage 4: finish the ownership transfer
Move the authoritative write responsibility under a documented cutover procedure. Confirm no legacy job, support script or consumer still writes through the old path. Keep the previous system available only according to the recovery design; do not leave two active owners accidentally.
CDC and data ownership during coexistence
Use CDC as one possible feed from the current authority into a target representation. Define the initial snapshot boundary, subsequent change position, ordering requirements and restart behavior. Test inserts, updates and deletes, including changes that occur while the initial load is running.
Track capture lag and consumer progress relative to retention. If a consumer falls beyond available history, a simple restart may not be enough. Define when to resnapshot, how to reconcile and who approves recovery.
Translate storage changes carefully. A table update may be one part of a larger transaction and may not correspond to a complete business event. Do not label every row update “OrderApproved” unless the business semantics actually support it.
CDC does not make writes to old and new systems atomic. Avoid naive dual writes where one succeeds and the other fails. Choose a source of truth at each stage and use a reliable propagation and reconciliation workflow appropriate to the chosen components.
Protect data and credentials in the replication path. Apply least privilege, control target access and review which sensitive fields must move. A migration feed should not silently expand access to the full legacy database.
A practical risk register
| Risk | Detection | Mitigation and owner |
|---|---|---|
| Hidden legacy writer | Write inventory and audit evidence | Capability owner removes or redirects it before authority transfer |
| Replication gap | Progress positions and reconciliation | Data owner rehearses resnapshot and replay |
| Duplicate external action | Operation identity and action reconciliation | Integration owner isolates shadow work and defines duplicate handling |
| Facade bottleneck | Routing latency, saturation and failure tests | Platform owner validates capacity and recovery |
| Contract drift | Old-client and characterization tests | API owner versions intentional behavior changes |
| Unsafe route reversal | Data compatibility checks | Release owner defines the last reversible boundary |
Each risk needs a trigger, accountable owner and action. “We will monitor” is incomplete if no one knows which signal causes a hold or what recovery can safely do.
Rollback is a staged data decision
| Migration stage | Potential recovery | Prerequisite |
|---|---|---|
| Seam wraps the old implementation | Revert routing or abstraction change | No incompatible contract or data change |
| Replacement serves reads | Return reads to the old path | Old data remains authoritative and current |
| Replacement accepts writes | Use reviewed reversal or forward recovery | Reconciliation and authority transfer rules are proven |
| Legacy data or behavior retired | Restore or forward repair through the approved plan | Backups, dependencies and recovery objectives are validated |
A feature flag can choose an implementation; it cannot make incompatible data readable or undo an external action. Define the point after which route reversal alone is insufficient.
For a fictional order-status extraction, keep the monolith as write authority while a new read service is populated and compared. Read routing can return to the old path if needed. Later, moving order updates requires a separate authority cutover and recovery rehearsal. Combining those stages into one switch would hide a major change in risk.
Before retirement, verify zero intended traffic, no remaining writes, required exports and retained recovery evidence. Remove obsolete credentials and transitional components through the normal change process. A permanent facade may still be useful, but it should then have a clear long-term role.
Use the module-boundary guide to assess extraction readiness and the controlled release guide for cohort and health-gate planning.
Common questions
How can you break apart a monolith without a big-bang rewrite?
Choose a bounded capability, introduce a route or code seam, replace behavior incrementally and transfer data ownership through tested stages.
Is Branch by Abstraction the same as a long-lived Git branch?
No. It uses an application abstraction to allow implementations to coexist while the main system continues to build and release.
Does CDC turn database changes into business events?
Not automatically. Captured row changes need interpretation and may represent only part of a business operation.
Can we roll back by changing the facade route?
Only while the previous path remains compatible and authoritative data is available. Write cutover can require reconciliation or forward recovery.
Must every monolith become microservices?
No. Extract only when an independent operational boundary provides clear value. A modular monolith may meet the business need.
Plan a migration with explicit ownership and recovery
Bring your capability inventory, dependencies and data flows. Discuss your .NET modernization roadmap to map staged replacement, risks and a credible recovery boundary.
