ストラングラーフィグパターン入門 - レガシーを段階的に置き換える戦略

2026.04.10

公式ドキュメント

この記事の要点

• ストラングラーフィグパターンはレガシーシステムを段階的に新システムに置き換える手法
• ファサード(プロキシ)を設置して新旧システムを共存させながら移行する
• ビッグバン置換と異なりリスクを最小化しながら近代化を実現

ストラングラーフィグパターンとは

ストラングラーフィグパターン(Strangler Fig Pattern)は、Martin Fowler が 2004 年に提唱したレガシーシステム移行のための設計パターンです。オーストラリアの熱帯雨林に生息する「絞め殺しイチジク(Strangler Fig)」という植物が、宿主の木を覆いながら徐々に置き換えていく様子に由来します。

このパターンの核心は、「レガシーシステムを一気に書き換える(ビッグバン置換)のではなく、新しいシステムを古いシステムの周りに少しずつ構築し、徐々に機能を移行していく」というアプローチにあります。最終的にレガシーシステムが完全に置き換わるまで、両者は共存します

flowchart LR
    subgraph Phase1["フェーズ1: 開始"]
        L1["Legacy System"]
    end
    subgraph Phase2["フェーズ2: ファサード設置"]
        F2["Facade / Proxy"] --> L2["Legacy"]
    end
    subgraph Phase3["フェーズ3: 部分移行"]
        F3["Facade"] --> L3["Legacy<br/>(縮小)"]
        F3 --> N3["New Service A"]
    end
    subgraph Phase4["フェーズ4: 完全移行"]
        F4["Facade"] --> N4["New Services"]
    end

なぜストラングラーフィグパターンが必要か

ビッグバン置換の失敗

レガシーシステムを「全部書き換える」プロジェクトの多くは失敗します。主な理由は以下です。

  • 完成までの数年間、ビジネス価値を提供できない
  • 旧システムの暗黙仕様を見落とすリスク
  • 移行直前まで本番投入されないため不具合発覚が遅れる
  • スコープが膨張し続け、終わりが見えなくなる
  • 旧システムへの新機能追加が同時並行で発生

段階的アプローチの利点

ストラングラーフィグでは、移行を小さな単位に分割し、新機能を徐々に旧システムから剥がしていきます。各段階で本番にデプロイされるため、リスクが局所化され、ビジネス価値も継続的に提供できます。

基本原則

1. ファサードを介して全アクセスを集中させる

最初に、レガシーシステムへのすべてのアクセスを「ファサード(プロキシ・API ゲートウェイ)」経由に切り替えます。これが移行のためのコントロールポイントになります。

2. 機能単位で少しずつ移行する

機能を 1 つずつ新システムに実装し、ファサードでルーティングを切り替えます。一度にすべてを置き換えません。

3. 双方向のデータ整合性を保つ

新旧システムが同じデータを参照するため、データ同期戦略(共有 DB、イベントストリーム、変更データキャプチャ)が必要になります。

4. 撤退可能性を保証する

各ステップは元に戻せるように設計します。新機能に問題があれば即座にレガシーに戻せる仕組みが安心感を生みます。

5. レガシーへの新規開発を凍結する

移行中もレガシーに機能を追加し続けると終わりません。可能な限り新規追加は新システム側で行います。

構成要素の詳細

ファサード / プロキシ層

リバースプロキシ、API ゲートウェイ、あるいはアプリケーション内のルーティング層が該当します。Nginx、Envoy、Kong、AWS API Gateway などが使われます。

ルーティングルール

URL パス、HTTP メソッド、ヘッダー、ユーザー属性などに基づいて、リクエストを新旧どちらに振り分けるかを決定します。

データ層の戦略

  • 共有データベース: 移行初期は旧 DB を共有
  • 二重書き込み: 新旧両方に書き込む
  • 変更データキャプチャ(CDC): 旧 DB の変更を新 DB に伝播
  • イベントソーシング: 新システムをイベント駆動で構築

モニタリングとフィーチャートグル

新ルートへの切り替えはフィーチャートグルで制御し、メトリクスで異常を検知したら即座に旧ルートに戻します。

flowchart TB
    Client["Client"] --> FG["Feature Gate / Proxy"]
    FG -->|"フラグOFF"| Legacy["Legacy"]
    FG -->|"フラグON"| New["New Service"]
    FG --> Mon["Monitoring"]
    Mon -->|"異常検知"| FG

実装例

Nginx によるシンプルなファサード

upstream legacy_app {
    server legacy.internal:8080;
}

upstream new_users_service {
    server users-service.internal:3000;
}

server {
    listen 80;

    # 新システムへ移行済みのエンドポイント
    location /api/users {
        proxy_pass http://new_users_service;
    }

    # それ以外はすべてレガシーへ
    location / {
        proxy_pass http://legacy_app;
    }
}

アプリケーションレベルのファサード(TypeScript)

import express from "express";
import { isFeatureEnabled } from "./feature-flags";

const app = express();

app.use("/api/users", async (req, res, next) => {
  if (await isFeatureEnabled("new-users-service", req.user?.id)) {
    return proxyTo("http://users-service.internal:3000", req, res);
  }
  return proxyTo("http://legacy.internal:8080", req, res);
});

app.use("/", (req, res) => proxyTo("http://legacy.internal:8080", req, res));

段階的移行のフェーズ例

// フェーズ管理
type MigrationPhase = "shadow" | "canary" | "split" | "complete";

interface RouteConfig {
  endpoint: string;
  phase: MigrationPhase;
  newServiceUrl: string;
  legacyUrl: string;
  canaryPercentage?: number;
}

const routes: RouteConfig[] = [
  {
    endpoint: "/api/users",
    phase: "complete",
    newServiceUrl: "http://users-svc",
    legacyUrl: "http://legacy",
  },
  {
    endpoint: "/api/orders",
    phase: "canary",
    newServiceUrl: "http://orders-svc",
    legacyUrl: "http://legacy",
    canaryPercentage: 10,
  },
  {
    endpoint: "/api/inventory",
    phase: "shadow",
    newServiceUrl: "http://inventory-svc",
    legacyUrl: "http://legacy",
  },
];

Shadow モード(並行実行)

async function shadowExecute(req: Request): Promise<Response> {
  const legacyPromise = callLegacy(req);
  const newPromise = callNew(req).catch((err) => {
    logShadowError(err);
    return null;
  });

  const [legacyRes, newRes] = await Promise.all([legacyPromise, newPromise]);

  if (newRes) {
    compareResponses(legacyRes, newRes);
  }

  // ユーザーには常にレガシーの応答を返す
  return legacyRes;
}

メリット

  • リスクの局所化: 機能単位で本番投入し、問題があれば局所的にロールバック可能
  • 継続的な価値提供: 移行中もユーザーが新機能を享受できる
  • 段階的な学習: 新システムの設計を運用しながら洗練できる
  • 撤退可能性: いつでも元に戻せる安心感
  • チームの並行作業: 機能単位で分担できる

デメリット

  • 移行期間の長期化: 一気に終わらせる場合より総時間は長くなりがち
  • 二重メンテナンス: 新旧両方を保守する負担
  • データ整合性の複雑さ: 同期戦略の設計と運用が必須
  • インフラコスト: ファサード層・両システムの並行稼働
  • 完了の判断が難しい: 「最後の数%」が長く残るリスク

ユースケース

  1. モノリスからマイクロサービスへの移行: 機能境界を切り出して個別サービス化
  2. 言語・フレームワークの刷新: PHP から Go へ、Java から Kotlin へ
  3. オンプレミスからクラウドへ: 機能別にクラウド移行
  4. データベースの置き換え: Oracle から PostgreSQL へ
  5. UI フレームワークの近代化: jQuery から React へ画面単位で置換

よくある落とし穴

ファサードがボトルネックになる

すべてのトラフィックが通る箇所なので、性能・可用性・スケーラビリティの設計が必須です。

移行が終わらない

「次の機能はまた来年」を続けると永遠に共存します。期限と完了基準をプロジェクト計画に明記します。

レガシーへの機能追加が止まらない

ビジネス側の要求で旧システムに機能を追加し続けると、移行対象が増え続けます。新規開発の凍結ルールを徹底します。

データの不整合

二重書き込みやイベント伝播の遅延でデータがズレることがあります。整合性チェックの仕組みを並行で走らせます。

ロールバック手順の不備

新ルートに不具合が出ても戻せないと、移行は失敗します。各段階でロールバック手順を文書化・訓練します。

他パターンとの比較

パターンアプローチ期間リスク適用シーン
Strangler Fig段階的に置き換え中〜長期大規模レガシーの近代化
Big Bang Rewrite一斉切り替え中期小規模、または完全な仕様変更
Branch by Abstractionコード内で抽象化して切替短〜中期内部実装の置換
Parallel Run新旧並行実行で結果比較中期クリティカルな業務ロジック
Anti-Corruption Layerレガシーとの境界に変換層継続的レガシーとの統合維持

ストラングラーフィグは Anti-Corruption Layer や Branch by Abstraction と組み合わせて使われることも多いです。

ベストプラクティス

1. 最初にファサードを設置する

何よりも先にすべての通信を経路化します。コントロールできなければ移行は始まりません。

2. 移行優先度を業務価値で決める

技術的に簡単な箇所からではなく、業務的に変更頻度が高い・障害が多い箇所から着手すると効果が早く現れます。

3. Shadow モードで検証する

新サービスを並行稼働させ、応答を比較することで本番投入前に挙動の違いを発見できます。

4. メトリクスで成果を可視化

「移行済みエンドポイント数」「レガシーへのトラフィック割合」をダッシュボードで追跡し、進捗を可視化します。

5. 完了基準を明文化する

どの状態になれば「移行完了」と言えるのかを最初に決めます。あいまいなままだと永久に続きます。

6. レガシーの理解に投資する

レガシーの仕様を読み解くリバースエンジニアリングは必須です。旧システムを軽視すると必ず罠にはまります。

ポイント: ストラングラーフィグパターンの成功の鍵は「ファサード(プロキシ)」の設計です。新旧システムへのルーティングを柔軟に制御できる仕組みを最初に構築しましょう。

まとめ

ストラングラーフィグパターンは、レガシーシステムの近代化における最も信頼性の高い戦略の一つです。ビッグバン置換の華やかさはありませんが、リスクを管理し、ビジネスを止めずに、確実にゴールへ近づける現実的なアプローチです。鍵となるのはファサードの設置、段階的なルーティング切り替え、データ整合性の戦略、そして「終わらせる意志」です。10 年残ったレガシーが、次の 10 年は新しい姿で活躍できるよう、計画的に絞め殺していきましょう。

さらに踏み込んだトピック

Anti-Corruption Layer の併用

新システムが旧システムの概念に汚染されないよう、両者の境界に「Anti-Corruption Layer(ACL)」を設置します。Eric Evans が DDD で提唱した概念で、レガシーのデータモデルを新しいドメインの言葉に翻訳する変換層です。これがないと、新システムが「旧テーブル名のまま」のような歪な設計になりがちです。

// 旧システムのレスポンス
interface LegacyCustomerDto {
  CUST_ID: string;
  CUST_NM: string;
  RGST_DT: string; // YYYYMMDD
}

// 新システムのドメインモデル
class Customer {
  constructor(
    public readonly id: string,
    public readonly name: string,
    public readonly registeredAt: Date,
  ) {}
}

// Anti-Corruption Layer
class CustomerAntiCorruption {
  static fromLegacy(dto: LegacyCustomerDto): Customer {
    const y = Number(dto.RGST_DT.slice(0, 4));
    const m = Number(dto.RGST_DT.slice(4, 6)) - 1;
    const d = Number(dto.RGST_DT.slice(6, 8));
    return new Customer(dto.CUST_ID, dto.CUST_NM, new Date(y, m, d));
  }
}

Change Data Capture を使った同期

旧 DB の変更を新 DB にリアルタイム同期するには、Debezium のような Change Data Capture(CDC)ツールが有効です。アプリケーションコードを変更せずに、データベースの WAL/binlog から変更イベントを取り出し、Kafka 経由で新システムに流せます。これにより、データ移行と新機能開発を並行できます。

Branch by Abstraction との使い分け

Strangler Fig はシステム境界(プロキシ・URL)でルーティングを切り替えるのに対し、Branch by Abstraction はコード内部に抽象インターフェースを挟み、実装をすり替えます。後者はモノリス内部の置換に向き、前者はサービス境界での置換に向きます。両者を組み合わせて、まず内部で抽象化し、その後サービスとして切り出すというアプローチも有効です。

Parallel Run で正しさを検証

決済計算のような業務クリティカルな箇所では、新旧両方を実行して結果を比較する Parallel Run(GitHub の Scientist ライブラリが有名)が役立ちます。本番のリアルなトラフィックで旧と新の出力差分を測定し、ずれがゼロになってから新ロジックに切り替えます。

async function parallelRun<T>(
  legacy: () => Promise<T>,
  candidate: () => Promise<T>,
  compare: (a: T, b: T) => boolean,
): Promise<T> {
  const legacyResult = await legacy();
  candidate()
    .then((c) => {
      if (!compare(legacyResult, c)) {
        logMismatch(legacyResult, c);
      }
    })
    .catch(logCandidateError);
  return legacyResult;
}

組織への影響

技術的に正しくても、組織が旧システムの保守チームと新システムの開発チームに分断されると移行は遅れます。Conway の法則を考慮し、移行プロジェクトでは両チームを統合するか、強い橋渡し役を置くことが重要です。

「終わらせる」ためのチェックリスト

  • レガシーへのトラフィックが 0% に近づいているか
  • 新システムが本番運用で十分な期間安定しているか
  • レガシーの停止に依存する別システムがないか
  • データの最終マイグレーションは完了しているか
  • 旧システムのインフラ廃止計画が立っているか
  • ドキュメントとチーム知識は新システムに移管されたか

これらをすべて満たして初めて「移行完了」と宣言できます。

段階別の実践ガイド

フェーズ 0: 準備期

  • レガシーシステムの依存関係マップを作成
  • ビジネスドメインのコンテキスト境界を特定
  • 主要メトリクス(リクエスト数、エラー率、レイテンシ)のベースラインを取得
  • 移行後の理想形(To-Be アーキテクチャ)の素描を作成
  • ステークホルダーへの説明と合意形成

この段階で「何が動いているか分からない」という状態を解消することが、後のすべての工程の前提になります。

フェーズ 1: ファサード導入期

  • リバースプロキシまたは API ゲートウェイを設置
  • すべての外部トラフィックをファサード経由に切り替え
  • ファサード自体の可観測性(メトリクス・ログ・トレース)を整備
  • カナリアデプロイの仕組みを準備

ここでは新システムをまだ作りません。「コントロールできる状態」を作るのが目的です。

フェーズ 2: 最初の機能切り出し期

  • 最も価値が高く、依存が少ない機能を選定
  • 新サービスを実装し、Shadow モードで本番トラフィックを並行処理
  • 結果の差分を監視し、十分な期間問題がないことを確認
  • カナリーリリースで徐々にトラフィック比率を上げる
  • 最終的に 100% を新サービスに切り替え

最初の成功は組織の自信になります。慎重に選んで確実に成功させます。

フェーズ 3: 加速期

  • 並行して複数の機能を移行
  • 共通のテンプレートやプラットフォームを整備
  • データ同期戦略(CDC、共有 DB、二重書き込み)を統一
  • レガシー側への新規開発を凍結

ここで失敗パターンが「移行ペースが上がらない」「新規追加が止まらない」です。経営層を巻き込んで凍結ルールを徹底します。

フェーズ 4: 終了戦

  • 残った機能の置換難易度を評価
  • 場合によってはレガシーロジックをそのまま新環境に持ってくる「リフト&シフト」を許容
  • レガシーインフラの停止計画を策定
  • 旧 DB のアーカイブ、ドキュメント整理、運用引継ぎ

「終わり」を曖昧にしないことが、ストラングラーフィグ最大の課題です。

さらに踏み込んだトピック

組織と Conway の法則

ソフトウェアアーキテクチャは組織構造を反映するという Conway の法則があります。レガシーをマイクロサービスに分解しても、組織が縦割りなら境界が綺麗にならず、結局モノリスのような相互依存に逆戻りします。Inverse Conway Maneuver(理想のアーキテクチャに合わせて組織を設計し直す)を併用するのが理想です。

ブラウン下開発との対比

Greenfield(新規開発)と Brownfield(既存改修)では難易度が桁違いです。ストラングラーフィグは Brownfield 開発の代表格であり、技術選定よりも「既存の制約とどう折り合いをつけるか」のスキルが問われます。ロックイン、暗黙仕様、廃止できない機能、文化的抵抗との戦いが日常になります。

失敗例から学ぶ

著名な失敗例として、Knight Capital の 2012 年の事故があります。古いコードを物理的に削除せずデプロイしたことで、リリースの一部サーバーが古いロジックで動き続け、45 分で 4.4 億ドルの損失を出しました。ストラングラーフィグでは「新旧が共存する状態」が長期間続くため、デプロイ手順とフィーチャートグルの厳格な管理が極めて重要であることを示す教訓です。

補助テクニックの紹介

Event Interception

レガシーがイベントを発行できない場合でも、データベース層でトリガーや CDC を使ってイベントを「合成」することができます。これにより、レガシー本体に手を入れずに新システムをイベント駆動で構築できます。

Asset Capture

レガシーの内部状態を定期的にスナップショットし、新システムに渡すアプローチです。リアルタイム同期が不要な参照系から始めるのに適しています。

Read Through Caching

新システムのデータベースを「キャッシュ」として位置づけ、見つからなければレガシーから取得して保存する方式です。徐々にデータが新システムに集まり、最終的に切り離せます。

まとめ(追記)

ストラングラーフィグパターンは銀の弾丸ではありません。長期間の組織的な努力を要し、技術以上に「終わらせる意志」「経営層の支援」「文化の変革」が成功の鍵を握ります。しかし、ビッグバン置換と比べて圧倒的にリスクが低く、ビジネスを止めずに進められる現実的な戦略です。レガシーは敵ではなく、長年ビジネスを支えてきた資産です。敬意を持って、計画的に、確実に新しい姿へと置き換えていきましょう。

注意: レガシーシステムの全機能を一度に移行しようとすると、ビッグバン置換と同じリスクを抱えます。機能単位で段階的に移行することが重要です。

実践メモ: 移行の優先順位は「ビジネス価値が高い」かつ「技術的に移行しやすい」機能から始めるのが効果的です。

参考リソース

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

メールで無料相談する
← 一覧に戻る