BACK

Legacy System Migration Principles and Patterns

- Evaluate what parts of the legacy system truly need migration, considering that up to 80% might be rarely used and could be streamlined or removed.
- Consider the depth of investment beyond code migration, including UI/UX improvements and domain model refinement to better align with current business language and processes.
- Migration offers an opportunity to address domain model drift between business concepts and existing code, fixing mismatches that occur over time.
- Beware of tight coupling and scattered domain logic common in legacy systems; migration should aim to enhance modularity and cohesion.
- Plan for observability from the start to monitor synchronization issues when running legacy and new systems in parallel.

Patterns and Data Synchronization Strategies

- The Strangler Fig pattern is common but not a one-size-fits-all; it assumes stable interfaces and simple routing, which may not hold when APIs and models change.
- Data synchronization between new and legacy systems is critical, with options including Change Data Capture (CDC), event publishing from code, batch import jobs, or shared databases. Each approach has trade-offs in context and business relevance.
- Domain-driven design concepts like Bounded Contexts ("Bubbles") and Anti-Corruption Layers enable incremental migration with translation between legacy and new domain models.
- Migration within a single service/domain can be incremental, migrating write or read operations first, or dealing with complex bidirectional synchronization, which is best avoided if possible.

PayFit Case Studies and Lessons Learned

- PayFit, an HR and payroll company with complex multi-country rules, has migrated its employee management domain through two major attempts: system B (initial extraction) and system C (current, refined migration).
- Domain model drift example: originally "employee" assumed one contract per employee, evolving into "collaborator" with multiple "working agreements" and multiple contract types, causing code inconsistencies and mismatch in terminology.
- First case (collaborator/employee): Chose "right model first" migration to gain ownership and reduce complexity, synchronizing bidirectionally with legacy systems despite complexity. Encountered issues like duplicate entities ("doublant") due to asynchronous data sync and inconsistent data from imports.
- Observability mechanisms (developer portal with event tracking and Snowflake search) are crucial for troubleshooting and correlating events across systems.
- Second case (contract entity): Used event republishing strategy to provide events for other teams by translating legacy events into the new domain model rather than letting other domains consume legacy events directly, maintaining clear domain ownership and source of truth.
- Challenges included inconsistent legacy event quality and translation complexity but enabled unlocking other domains without full system rebuild upfront.

Actionable Items / Tasks

- Before migrating, conduct a thorough audit of legacy system usage to identify unnecessary components for removal.
- Align migration objectives with business goals and assess if modernization can improve business processes and user experience.
- Map domain models and business terminology to identify mismatches and design updated models accordingly.
- Evaluate and select suitable migration patterns and data synchronization strategies given the domain, system complexity, and integration needs.
- Prioritize observability infrastructure from day one for effective monitoring of dual system operations.
- Foster leadership support to ensure resources and strategic backing for long-term migration success.
- Incrementally migrate services, with careful consideration of synchronizing write and read operations to avoid complex bidirectional data conflicts.
- Implement domain-driven design practices like bounded contexts and anti-corruption layers to gradually replace legacy components.
- Use event-driven approaches to decouple legacy and new systems, ensuring each domain maintains ownership of its events and data.
- Continuously investigate and resolve root causes of synchronization issues to prevent persistent data inconsistencies.
- Leverage internal tooling for event tracing and investigation to handle issues such as duplicate entities or partial data sync.
- Resist shortcuts such as sharing legacy events between domains to avoid future migration burdens.

Legacy Architecture Migration Patterns

Share:

10:40 - 11:10, 27th of May (Tuesday) 2025 / DEV ARCHITECTURE STAGE

We all struggle with legacy code and want to modernize our old systems. But the journey from the architecture we have now to the shiny new modernized system we want to have can be long.

Along the journey we have to choose patterns for migrating from the current to the new architecture. Most likely we will have new and old running in parallel for many months or multiple years. That means we need to keep multiple systems and datastores in sync. To do so, there are many patterns we can choose from and many risks and trade-offs we need to consider.

You've probably heard of the strangler fig pattern, but the topic is far deeper and more nuanced than that. For example: you could have all write operations going to the legacy and all read operations to the new system or vice-versa? Or you could allow write operations in both and have bi-directional synchronisation. And there are many variations and shades of grey. 

The patterns you choose will have a tangible impact on your modernization journey. In this talk Nick will share real examples he has encountered over the past 15 years, including recent examples from his time at PayFit, so you can better decide which patterns are optimal in your context.

LEVEL:
Basic Advanced Expert
TRACK:
Dev Software Software Architecture
TOPICS:
MicroServices

Nick Tune

PayFit