モノリシックアーキテクチャからマイクロサービスへの進化は、段階的な移行戦略の重要な必要性を生み出しました。特にOracleやSQL Serverのレガシーシステムを持つ組織は、完全に停止するという贅沢を持つことはできません。 この質問は、企業が長年の歴史的データの整合性を犠牲にせず、数時間続くメンテナンスウィンドウを受け入れることなく、近代化を必要とする現実のシナリオから生まれました。
核心的な課題は、複数のドメインにわたるモノリシックACIDトランザクションとマイクロサービスの分散性とのインピーダンスミスマッチにあります。データベースを分解する際、レガシーシステムと新しいサービスの両方で同時に更新が発生するスプリットブレインシナリオに直面します。ネットワーク境界を越えて参照整合性を維持しながら両方のシステムを稼働させることは、単純なデータベースレプリケーションでは解決できない分散的コンセンサスの問題を生み出します。
変更データキャプチャ (CDC) を利用した イベント駆動アーキテクチャ を実装し、信頼性のあるイベント公開を確保するために アウトボックスパターンを使用します。レガシーデータベースのトランザクションログから行レベルの変更をキャプチャするために Debezium コネクタを展開し、中央神経系として Apache Kafka にイベントをストリーミングします。同時に、マイクロサービスレイヤーで サガパターンを実装して分散トランザクションを処理し、各サービスの運用の自律性を維持しながら最終的な整合性を確保します。
あるフォーチュン500の電子商取引プラットフォームは、10年以上前の Oracle モノリスから PostgreSQL ベースのマイクロサービスへオーダーマネジメントシステムを移行する必要がありました。インベントリ、価格設定、およびオーダー処理のモジュールは、12の主要テーブルにわたる外部キー制約で緊密に結合されていました。ホリデーシーズン中、システムはデータロスやダウンタイムの許容ゼロで50,000トランザクションを処理しました。
解決策A: デュアルライト戦略
エンジニアチームは、レガシーアプリケーションコードを修正して、Oracle と新しい PostgreSQL サービスに同時に書き込むことを最初に検討しました。このアプローチは書き込みを同期的で一貫して保持することで簡素化を約束しました。しかし、これは壊滅的な結合リスクをもたらしました—新しいサービスがレイテンシや失敗を経験した場合、古いシステム全体がクラッシュすることになります。また、XAプロトコルを介した分散トランザクションの実装は、パフォーマンスを著しく低下させ、ピーク時の応答時間が400%増加する可能性がありました。
解決策B: データベーストリガーとビュー
別のオプションは、Oracle内にデータベーストリガーを作成し、行の変更が認められた際にRESTエンドポイントを直接呼び出すことを含みました。これは、アプリケーションの変更を必要としないため魅力的でした。しかし、これはデータベースインフラストラクチャとネットワークトポロジーの間に緊密な結合を生み出し、システムを脆弱にしました。マイクロサービスエンドポイントに到達できない場合、トリガーは失敗し、レガシートランザクション全体がロールバックされ、ゼロダウンタイム要件に違反することになります。さらに、トリガーが特定の列構造に依存する場合、スキーママイグレーションの管理はほぼ不可能になりました。
解決策C: 変更データキャプチャとイベントソーシング
選択されたアーキテクチャは、Debeziumを利用してOracleのリドーログを監視し、すべての挿入、更新、削除を不変のイベントとしてApache Kafkaに公開しました。マイクロサービスは、これらのイベントをKafka Streamsを介して消費し、アウトボックスパターンを使用してPostgreSQLに変換および永続化し、正確には一度だけのセマンティクスを保証しました。Confluentによって管理されるスキーマレジストリは、Avroスキーマを使用して、前方および後方の互換性を強制しました。これにより、レガシーシステムは移行の複雑さから切り離され、Oracleは新しいアーキテクチャを認識しないまま、サービスは自分のペースでイベントを消費できました。
選ばれた解決策および理由
チームは解決策Cを選択しました。なぜなら、単一責任原則を尊重し、障害隔離を提供したからです。デュアルライトとは異なり、レガシーシステムのパフォーマンスはマイクロサービスのレイテンシによって影響を受けませんでした。トリガーと比較して、Debeziumは非同期動作し、トランザクションをブロックしませんでした。イベントログはimmutableな監査証跡を提供し、Kafkaの保持ポリシーにより、スキーマの進化中にマイクロサービスが再処理する必要がある場合に過去のデータを再生することが可能でした。
結果
8ヶ月の移行の後、プラットフォームは200TBのトランザクショナルデータを99.97%の稼働時間で移行することに成功しました。システムは、前年度より40%遅延が低い状態でブラックフライデーのトラフィックを処理しました。新サービスで価格計算バグが見つかったとき、チームはレガシーのOracleシステムに触れることなくKafkaから3日分のイベントを再生し、2.3百万レコードをダウンタイムなしで修正しました。CDCパイプラインは現在、Apache Flinkを使用したリアルタイム分析の基盤として機能しています。
モノリスがテーブル構造を変更し、マイクロサービスがCDCイベントを消費する際、スキーマの進化にどのように対処しますか?
候補者はしばしば移行中にスキーマを凍結することを提案しますが、これはアジャイルなビジネスには非現実的です。正しいアプローチは、Confluent Schema Registryを実装し、前方および後方の互換性モードを使用してAvroスキーマを使うことです。Oracleテーブルが変更されると、Debeziumコネクタは更新されたスキーマと共にイベントを公開しますが、レジストリは互換性ルールを強制します。サービスはApache Avroの解決ルールを使用してSchema-on-Readパターンを実装するべきです—未知のフィールドを無視し、欠落した値にはデフォルト値を使用します。加えて、リードモデルがソーススキーマとは独立して進化できるCQRSパターンを展開し、ネストした構造が消費エンドポイントに到達する前に平坦化するKafka Connectトランスフォーマーを使用します。
移行期間中に両方のシステムが同じエンティティを同時に更新した場合はどうなりますか?
これにより、単純なタイムスタンプでは解決できないスプリットブレインシナリオが発生します。アーキテクトは、決定論的な競合解決のためにベクトルクロックまたはCRDTs(Conflict-free Replicated Data Types)を実装する必要があります。マイクロサービスイベントを消費し、Kafka Connect JDBC Sinkを使用してOracleに書き戻す双方向同期コンポーネントを展開しますが、Hybrid Logical Clocksに基づく厳密な**Last-Write-Wins (LWW)**セマンティクスを維持します。
さらに重要なのは、ドメイン駆動設計の境界を実装することです—移行中、集約ルートごとにモノリスまたはマイクロサービスのどちらかに単独の書き込み権限を割り当て、両方に決して権限を与えないでください。レガシーのOracleで移行状態を示すデータベースフラグを使用し、トラフィックを適切にルーティングします。API Gatewayを通じてStrangler Fig Patternを使用します。
レガシーデータベースと新しいマイクロサービスにまたがるビジネス操作のトランザクション整合性を確保するためのパターンを説明してください。
ほとんどの候補者は、異種システム間でのTwo-Phase Commit (2PC)を使用した分散トランザクションを誤って提案し、これは脆弱な結合および可用性の問題を引き起こします。適切な解決策は、Compensating Transactionsを持つサガパターンを使用します。ユーザーアクションがOracle(レガシー)とPostgreSQL(新)両方の更新を必要とする場合、これはCamundaまたはTemporalに基づいたサガオーケストレーターを通じて調整されます。このプロセスは、最初にOracleを更新し、その後ドメインイベントを発行し、最後にマイクロサービス操作を実行する形でローカルトランザクションを順次実行します。いずれかのステップが失敗した場合は、補償トランザクションを実行します—マイクロサービスのコミットが失敗した場合、レガシーシステムがOracleの変更を元に戻すためのロールバックイベントを消費するトリガーを発生させます。これにより、ネットワーク境界を越えてリソースをロックせずに最終的な整合性を維持します。