ストanglerフィグパターンによるモノリスの段階的な分離
Olivia Novak
Dev Intern · Leapcell

はじめに
ソフトウェア開発の急速に進化する状況において、かつてエンタープライズアーキテクチャの基盤であったモノリシックアプリケーションは、しばしばボトルネックとなります。その固有の密接に結合された性質は、アジリティ、スケーラビリティ、および保守性を妨げる可能性があります。組織がより高い柔軟性とより速いデリバリーサイクルを目指すにつれて、マイクロサービスアーキテクチャへの魅力はますます強まっています。しかし、モノリスからマイクロサービスへの完全な書き換えに直接飛び込むことは、リスク、高コスト、およびプロジェクトの失敗の可能性に満ちた危険な試みです。そこで、ストanglerフィグパターンが実用的かつ効果的な戦略として登場します。それは、モノリシックアプリケーションを独立してデプロイ可能なサービスのスイートに分解するための構造化された段階的なアプローチを提供し、混乱とリスクを最小限に抑えます。この記事では、ストanglerフィグパターンを掘り下げ、バックエンドチームがどのようにそれを利用して、モノリシックな巨人からアジャイルなマイクロサービスエコシステムへと安全かつ成功裏に移行できるかを検討します。
本文
さらに深く掘り下げる前に、議論の中心となるいくつかの重要な用語を明確に理解しておきましょう。
- モノリシックアプリケーション(Monolithic Application): すべてのコンポーネントが密接に結合されており、単一のプロセス内で実行される、単一の自己完結型ユニットとして設計されたソフトウェアアプリケーション。
- マイクロサービスアーキテクチャ(Microservices Architecture): アプリケーションを、それぞれ特定のビジネス機能に責任を持つ、疎結合で独立してデプロイ可能なサービスのコレクションとして構造化するアーキテクチャスタイル。
- ストanglerフィグパターン(Strangler Fig Pattern): 既存のモノリスと並行して新しいシステム機能がマイクロサービスとして構築される、段階的なリファクタリング手法。時間の経過とともに、特定の機能に対するモノリスへの呼び出しが新しいマイクロサービスにリダイレクトされ、古いモノリスを効果的に「絞め殺して」最終的に廃止できるようにします。このパターンは、宿主の木を囲んで成長し、最終的にそれを置き換えるストanglerフィグの木に触発されています。
- アンチコルプションレイヤー(Anti-Corruption Layer - ACL): レガシーシステム(モノリス)と新しいシステム(マイクロサービス)間の通信を翻訳し、新しいシステムのドメインモデルがレガシーシステムの複雑さや矛盾によって汚染されないように保護するレイヤー。
ストanglerフィグの原則
ストanglerフィグパターンの中心的な原則は、段階的な置き換えです。既存のシステムを放棄して新しいシステムを一から構築する「ビッグバン」リライトの代わりに、ストanglerフィグパターンは、モノリスと並行してマイクロサービスとして新しい機能の構築や既存部分の移植を提唱しています。その後、トラフィックは段階的にモノリスからこれらの新しいサービスにリダイレクトされます。
ユーザー認証、製品カタログ、注文処理、支払いなどを処理するモノリシックなeコマースアプリケーションを想像してみてください。ストanglerフィグパターンを使用すると、システム全体を一度に書き換えるわけではありません。代わりに、「ユーザー認証」モジュールを抽出の良い候補として特定するかもしれません。
- 境界付けられたコンテキスト(Bounded Context)の特定: モノリス内で、自然に分離してマイクロサービスとして開発できる特定の機能領域を特定します。認証は、明確な境界があるため、一般的な出発点です。
- 新しいサービスの構築: ユーザー認証のみを担当する新しいマイクロサービスを開発します。このサービスには、独自のデータベース、API、およびデプロイメントパイプラインがあります。
- ファサード/APIゲートウェイの実装: モノリスと新しいマイクロサービスの両方の前に配置されるAPIゲートウェイまたはファサードレイヤーを導入します。当初は、すべてのリクエストがモノリスを経由する可能性があります。
- トラフィックのリダイレクト: 特定の機能(例:
/api/auth/*)に関連するリクエストを、モノリスから新しいマイクロサービスに段階的にリダイレクトします。このリダイレクトは、さまざまなレイヤーで行うことができます。- ロードバランサー/リバースプロキシ: 特定のURLパスを新しいサービスにルーティングするようにロードバランサー(例: Nginx, HAProxy)を設定します。
- アプリケーションレベルのファサード: モノリス自体内で、プロキシとして機能し、特定の操作のために新しいマイクロサービスを呼び出す新しいモジュールを作成します。これはA ACLの一部であることがよくあります。
- 洗練と繰り返し: 新しいサービスが成熟し、安定していることが証明されるにつれて、より多くの機能が抽出される可能性があります。モノリスが最終的に最小限のコアに縮小されるか、完全に置き換えられるまで、プロセスは他の境界付けられたコンテキスト(例: 製品カタログ、注文管理)に反復的に適用されます。
実践的な実装例
ストanglerフィグパターンを使用してリファクタリングしたい、Flaskで書かれたシンプルなモノリシックなユーザー管理システムを考えてみましょう。
元のモノリス構造(簡略化されたPython/Flask)
# monolith_app.py from flask import Flask, request, jsonify app = Flask(__name__) users_db = { "john.doe": {"password": "password123", "email": "john@example.com"}, "jane.smith": {"password": "securepwd", "email": "jane@example.com"} } @app.route('/users/<username>', methods=['GET']) def get_user(username): user_data = users_db.get(username) if user_data: return jsonify({"username": username, "email": user_data["email"]}), 200 return jsonify({"message": "User not found"}), 404 @app.route('/auth/login', methods=['POST']) def login(): data = request.json username = data.get('username') password = data.get('password') user_data = users_db.get(username) if user_data and user_data['password'] == password: return jsonify({"message": "Login successful", "token": "dummy_jwt_token"}), 200 return jsonify({"message": "Invalid credentials"}), 401 if __name__ == '__main__': app.run(port=5000)
次に、login機能を別のマイクロサービスに抽出しましょう。
新しい認証マイクロサービス(authentication_service.py)
# authentication_service.py from flask import Flask, request, jsonify auth_app = Flask(__name__) # このマイクロサービスは、モノリスとは異なる独自のユーザーデータベースを持つでしょう。 # デモンストレーションのために、簡略化されたバージョンを使用します。 auth_users_db = { "john.doe": {"password": "password123"}, "jane.smith": {"password": "securepwd"} } @auth_app.route('/authenticate', methods=['POST']) def authenticate_user(): data = request.json username = data.get('username') password = data.get('password') user_data = auth_users_db.get(username) if user_data and user_data['password'] == password: # 実際のシナリオでは、これは適切なJWTを生成して返します return jsonify({"message": "Authentication successful", "token": f"auth_token_for_{username}"}), 200 return jsonify({"message": "Invalid authentication credentials"}), 401 if __name__ == '__main__': auth_app.run(port=5001) # 別のポートで実行
APIゲートウェイ(またはリバースプロキシ設定)の導入
モノリスのloginエンドポイントを直接変更する代わりに、トラフィックをルーティングするAPIゲートウェイ(例: Nginx, Kong、または単純なPython Flaskプロキシ)を導入します。
Nginx設定例(APIゲートウェイのシミュレーション)
# nginx.conf 関連スニペット http { upstream monolith_backend { server 127.0.0.1:5000; } upstream auth_service_backend { server 127.0.0.1:5001; } server { listen 80; # /api/auth/login を新しい認証サービスにルーティング location /api/auth/login { proxy_pass http://auth_service_backend/authenticate; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # その他の /api リクエストはモノリスへ location /api/ { proxy_pass http://monolith_backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
このNginx設定により、http://your-domain/api/auth/loginへのリクエストは、ポート5001で実行されている新しい認証マイクロサービス(/authenticateエンドポイントにマッピング)に誘導されます。他のすべての/api/リクエストは、ポート5000の元のモノリスに引き続き送信されます。
実践におけるアンチコルプションレイヤー
新しいマイクロサービスがモノリスにコールバックする必要がある場合、またはその逆の場合、アンチコルプションレイヤー(ACL)が重要になります。たとえば、認証サービスがモノリスのデータベースにまだ排他的にあるユーザー詳細を必要とする場合、ACLは、このデータを取得するために認証サービス内に実装できます。
# authentication_service.py のロジック内、または専用クライアントモジュール import requests MONOLITH_BASE_URL = "http://127.0.0.1:5000" def get_user_details_from_monolith(username): """ モノリスからユーザー詳細を取得するためのアンチコルプションレイヤー関数。 モノリスのデータ構造を認証サービスが期待する形式に翻訳します。 """ try: response = requests.get(f"{MONOLITH_BASE_URL}/users/{username}") response.raise_for_status() # 無効なステータスコードに対して例外を発生させる monolith_data = response.json() # モノリス構造を認証サービスのドメインモデルに翻訳 # 例: モノリスは 'email' を返すかもしれませんが、認証サービスは 'username' のみに関心があるかもしれません return {"username": monolith_data.get("username"), "email_from_monolith": monolith_data.get("email")} except requests.exceptions.RequestException as e: print(f"Monolithからユーザー取得時のエラー: {e}") return None # authenticate_user 内で: # ... # user_data = auth_users_db.get(username) # if not user_data: # monolith_user_info = get_user_details_from_monolith(username) # if monolith_user_info and monolith_user_info.get("email_from_monolith"): # # ユーザーを移行するか、一時的なログインに使用する # pass # モノリスでユーザーが見つかり、ログインを処理 # ...
このACLは、認証マイクロサービスがモノリスの内部データ構造を直接使用せず、独立性とクリーンなドメインモデルを維持するようにします。
アプリケーションシナリオ
ストanglerフィグパターンは、次のようなシナリオで特に効果的です:
- 高いビジネス価値を持つレガシーシステム: モノリスがビジネスオペレーションにとって重要である場合、ビッグバンリライトはリスクが高すぎます。
- 段階的なモダナイゼーション: チームは、アプリケーション全体に影響を与えることなく、システムの一部に新しいテクノロジー(例: 新しい言語、フレームワーク、データベース)を採用したいと考えています。
- チームの自律性: 異なるチームが個々のサービスを所有し、独立して開発できるようにし、自律性とより速いイテレーションを促進します。
- 技術的負債の削減: 新しく構築されたサービスでそれらを置き換えることにより、技術的負債が高い領域を体系的に対処します。
結論
ストanglerフィグパターンは、モノリシックアーキテクチャからマイクロサービスへの安全な移行のための堅牢で実用的なロードマップを提供します。段階的な抽出、慎重なトラフィックリダイレクト、およびアンチコルプションレイヤーの戦略的な使用を採用することにより、組織はリスクを最小限に抑え、ビジネスの継続性を維持し、よりアジャイルでスケーラブルな未来に向けてシステムを徐々に進化させることができます。このパターンは根本的に革命よりも進化を支持しており、エンタープライズアプリケーションのモダナイゼーションのための中心的な戦略となっています。

