Skip to content
advanced Phase 111 · Migration

Incremental Migration in Magento 2

Feature-by-feature migration strategies including data synchronization and dual-write patterns

45m
2 problems
Topic Progress 0%

Feature-by-Feature Migration

Decomposing the Monolith

Feature Map

Current Monolith:
┌─────────────────────────────────────┐
│         Legacy Magento              │
├─────────┬─────────┬─────────┬──────┤
│ Catalog │ Orders  │ Payments│ Users │
│ - Products│ - Create│ - Stripe│ - Auth│
│ - Search │ - Status│ - PayPal│ - ACL │
│ - Rules  │ - Refund│ - Invoice│ - API │
└─────────┴─────────┴─────────┴──────┘

Target: Migrate features independently

Migration Order (by dependency):
1. User Authentication (no dependencies)
2. Product Catalog (depends on users)
3. Cart (depends on catalog)
4. Orders (depends on cart, payments)
5. Payments (depends on orders)

Migration Priority Matrix

Feature        | Business Value | Complexity | Dependencies | Priority
---------------|---------------|------------|-------------|----------
User Auth      | High          | Low        | None        | P1
Product Catalog| High          | Medium     | Users       | P1
Search         | Medium        | Medium     | Catalog     | P2
Cart           | High          | Medium     | Catalog     | P1
Checkout       | High          | High       | Cart, Pay   | P2
Orders         | High          | High       | Checkout    | P3
Payments       | High          | High       | Orders      | P3
Admin Panel    | Medium        | Medium     | All         | P4

Feature Migration Steps

For each feature:
1. Define interface (API contract)
2. Build new implementation
3. Write tests for both old and new
4. Implement data sync
5. Route traffic gradually
6. Verify consistency
7. Remove old code
8. Update documentation

Estimation per Feature

$featureEstimates = [
    'user_auth' => [
        'new_code' => 40,      // hours
        'tests' => 16,
        'data_sync' => 8,
        'integration' => 16,
        'total' => 80
    ],
    'product_catalog' => [
        'new_code' => 80,
        'tests' => 32,
        'data_sync' => 24,
        'integration' => 24,
        'total' => 160
    ],
];

Data Synchronization

Sync Strategies

Real-Time Sync

// Event-based synchronization
class OrderSyncService
{
    public function __construct(
        private EventManager $eventManager,
        private LegacyOrderRepository $legacyRepo,
        private NewOrderRepository $newRepo
    ) {
        $this->eventManager->dispatch('order_save_after', [
            'order' => $order,
            'sync_callback' => [$this, 'syncOrder']
        ]);
    }
    
    public function syncOrder(OrderInterface $order): void
    {
        $newOrder = $this->convertOrder($order);
        $this->newRepo->save($newOrder);
    }
}

Batch Sync

// Nightly batch synchronization
public function syncOrdersBatch(int $batchSize = 1000): void
{
    $lastId = $this->getLastSyncedId();
    
    do {
        $orders = $this->legacyRepo->getOrdersAfter($lastId, $batchSize);
        
        foreach ($orders as $order) {
            $this->syncOrder($order);
            $lastId = $order->getId();
        }
        
        $this->updateLastSyncedId($lastId);
        
    } while (count($orders) === $batchSize);
}

Conflict Resolution

// Handle sync conflicts
public function resolveConflict(
    OrderInterface $legacy,
    OrderInterface $new
): OrderInterface {
    // Strategy 1: Last write wins
    if ($legacy->getUpdatedAt() > $new->getUpdatedAt()) {
        return $this->convertToNew($legacy);
    }
    
    // Strategy 2: Legacy wins during migration
    if ($this->isLegacyPrimary()) {
        return $this->convertToNew($legacy);
    }
    
    // Strategy 3: Manual resolution
    $this->flagForReview($legacy, $new);
    return $new; // Default to new
}

Sync Monitoring

// Monitor sync health
public function getSyncStatus(): array
{
    return [
        'legacy_count' => $this->legacyRepo->count(),
        'new_count' => $this->newRepo->count(),
        'synced_count' => $this->getSyncedCount(),
        'pending_count' => $this->getPendingCount(),
        'error_count' => $this->getErrorCount(),
        'last_sync' => $this->getLastSyncTime(),
        'sync_lag' => $this->getSyncLag(),
    ];
}

Dual-Write Patterns

Writing to Both Systems

Synchronous Dual-Write

// Write to both systems atomically
public function saveOrder(OrderInterface $order): void
{
    $transaction = $this->transactionFactory->create();
    
    try {
        // Write to new system
        $this->newOrderRepo->save($order);
        
        // Write to legacy system
        $legacyOrder = $this->convertToLegacy($order);
        $this->legacyOrderRepo->save($legacyOrder);
        
        $transaction->commit();
    } catch (\Exception $e) {
        $transaction->rollBack();
        throw $e;
    }
}

Asynchronous Dual-Write

// Write to primary, queue secondary
public function saveOrder(OrderInterface $order): void
{
    // Write to primary (new system)
    $this->newOrderRepo->save($order);
    
    // Queue for legacy sync
    $this->messageQueue->publish('order.sync', [
        'order_id' => $order->getId(),
        'action' => 'save',
        'timestamp' => time()
    ]);
}

// Consumer process
public function processSyncMessage(array $message): void
{
    $order = $this->newOrderRepo->get($message['order_id']);
    $legacyOrder = $this->convertToLegacy($order);
    $this->legacyOrderRepo->save($legacyOrder);
}

Switching Primary System

// Gradually switch which system is primary
public function saveOrder(OrderInterface $order): void
{
    if ($this->isNewSystemPrimary()) {
        // New is primary, write to legacy async
        $this->newOrderRepo->save($order);
        $this->queueLegacySync($order);
    } else {
        // Legacy is primary, write to new async
        $this->legacyOrderRepo->save($order);
        $this->queueNewSync($order);
    }
}

Error Handling

// Handle dual-write failures
public function saveOrderWithRetry(OrderInterface $order, int $maxRetries = 3): void
{
    for ($i = 0; $i < $maxRetries; $i++) {
        try {
            $this->saveOrder($order);
            return;
        } catch (\Exception $e) {
            $this->logger->warning('Dual-write failed', [
                'attempt' => $i + 1,
                'error' => $e->getMessage()
            ]);
            
            if ($i === $maxRetries - 1) {
                $this->flagForManualReview($order, $e);
                throw $e;
            }
            
            sleep(pow(2, $i)); // Exponential backoff
        }
    }
}

Data Validation

Consistency Verification

Automated Validation

// Verify data consistency between systems
public function validateOrderConsistency(int $orderId): ValidationResult
{
    $legacy = $this->legacyRepo->get($orderId);
    $new = $this->newRepo->get($orderId);
    
    $diffs = [];
    
    // Compare fields
    $fields = ['grand_total', 'status', 'customer_email', 'shipping_method'];
    
    foreach ($fields as $field) {
        $legacyValue = $legacy->getData($field);
        $newValue = $new->getData($field);
        
        if ($legacyValue != $newValue) {
            $diffs[] = [
                'field' => $field,
                'legacy' => $legacyValue,
                'new' => $newValue
            ];
        }
    }
    
    return new ValidationResult(
        count($diffs) === 0,
        $diffs
    );
}

Bulk Validation

// Validate all synced orders
public function validateAllSynced(int $limit = 10000): array
{
    $results = [
        'total' => 0,
        'consistent' => 0,
        'inconsistent' => 0,
        'missing_legacy' => 0,
        'missing_new' => 0
    ];
    
    $orderIds = $this->getSyncedOrderIds($limit);
    
    foreach ($orderIds as $orderId) {
        $results['total']++;
        
        $legacy = $this->legacyRepo->get($orderId);
        $new = $this->newRepo->get($orderId);
        
        if (!$legacy) {
            $results['missing_legacy']++;
            continue;
        }
        
        if (!$new) {
            $results['missing_new']++;
            continue;
        }
        
        if ($this->validateOrderConsistency($orderId)->isConsistent()) {
            $results['consistent']++;
        } else {
            $results['inconsistent']++
        }
    }
    
    return $results;
}

Reconciliation Reports

// Generate reconciliation report
public function generateReport(): string
{
    $report = "Data Reconciliation Report\n";
    $report .= str_repeat('=', 50) . "\n\n";
    
    $results = $this->validateAllSynced();
    
    $report .= "Total Orders Validated: {$results['total']}\n";
    $report .= "Consistent: {$results['consistent']}\n";
    $report .= "Inconsistent: {$results['inconsistent']}\n";
    $report .= "Missing in Legacy: {$results['missing_legacy']}\n";
    $report .= "Missing in New: {$results['missing_new']}\n\n";
    
    $consistencyRate = ($results['consistent'] / $results['total']) * 100;
    $report .= sprintf("Consistency Rate: %.2f%%\n", $consistencyRate);
    
    if ($results['inconsistent'] > 0) {
        $report .= "\nInconsistent Orders:\n";
        $inconsistent = $this->getInconsistentOrders();
        foreach ($inconsistent as $order) {
            $report .= "- Order #{$order['id']}: {$order['diff_count']} differences\n";
        }
    }
    
    return $report;
}

Practice Problems

0 / 2 solved
Feature Migration Plan

Create a feature-by-feature migration plan for migrating the product catalog from legacy to new system.

Data Sync Implementation

Implement real-time data synchronization between legacy and new order systems with conflict resolution.

Quiz

1. What should be migrated first in feature-by-feature approach?

Question 1 options

2. What is dual-write?

Question 2 options

3. How to handle sync conflicts?

Question 3 options

4. What does data reconciliation verify?

Question 4 options

Flashcards

Question

What is feature-by-feature migration?

Answer

Migrating one feature at a time, starting with those having no dependencies

Question

What is dual-write?

Answer

Writing to both old and new systems during migration transition

Question

How to handle sync conflicts?

Answer

Last write wins, primary system preference, or manual review

Question

What is data reconciliation?

Answer

Verifying data consistency between legacy and new systems

Question

What is sync lag?

Answer

The delay between writing to primary system and it appearing in secondary system

Revision Notes

Key Takeaways

  • 1. Migrate features with no dependencies first to build foundation
  • 2. Dual-write keeps both systems in sync during transition
  • 3. Real-time sync uses events; batch sync runs on schedule
  • 4. Conflict resolution strategies: last write wins, primary preference, manual review
  • 5. Data reconciliation must be automated and run continuously
  • 6. Monitor sync lag and error rates during migration

Interview Tips

  • How do you decide which features to migrate first?
  • Explain dual-write pattern and its trade-offs
  • How do you handle data sync conflicts?
  • What metrics do you track during incremental migration?
  • How do you validate data consistency between systems?

Cheat Sheet

Incremental Migration Cheat Sheet

Migration Order:

  1. No dependencies first
  2. Build foundation
  3. Layer dependent features

Sync Strategies:

  • Real-time: event-based
  • Batch: scheduled job
  • Queue: asynchronous

Dual-Write:

  • Synchronous: atomic both
  • Async: primary first, queue secondary
  • Monitor: sync lag, errors

Validation:

  • Automated field comparison
  • Bulk reconciliation
  • Reconciliation reports
  • Continuous monitoring