現在働いている会社のサービスは MSA(マイクロサービスアーキテクチャ)構造になっています。
仕事をしながら経験した MSA の開発面でのメリットとデメリットを整理してみました。
ただし、インフラの観点ではなく、サーバーアプリケーションの開発という視点で考えていただければと思います。
メリット 1. 担当するサーバーへの素早い適応が可能
従来のモノリシック構造では、すべての機能を1つのサーバーアプリケーション に実装しますが、MSA 構造では ドメインごとにサーバーを分割して機能を実装します。
もし他のドメインサーバーのデータが必要な場合は、サーバー間通信を通じて必要なデータを取得する形で実装されます。
そのため、自分が担当するサーバーの機能以外については、他のサーバーがどのように機能を実装しているかを気にする必要がなく、必要なデータを取得するために他のサーバーの API を呼び出せばよいという利点があります。
この特性により、新しいメンバーがチームに参加した際、プロジェクト全体のロジックを完全に把握していなくても、担当するサーバーのロジックだけを理解すればよいため、短期間でプロジェクトに適応しやすくなります。
実際、私も Git の権限を付与されてから 1 週間後には、少しずつ機能を実装することができました。
メリット 2. ドメインごとのサーバー分離による開発効率の向上
MSA はドメインごとにサーバーを分離するため、特定のドメインサーバーが停止しても、他のドメインサーバーには直接的な影響を与えません。
例: ユーザー管理サーバーと決済管理サーバーが分離されている場合、
ユーザー管理サーバーが停止しても、決済管理サーバーは影響を受けずに動作します。
(ただし、両方のサーバーが連携する機能は利用できなくなります。)
モノリシック構造では、同じプロジェクト内で開発を進めるため、1 人が特定の機能を修正・デプロイする際に 他の部分と衝突が発生するリスクがあります。
しかし、MSA 構造ではドメインごとにサーバーが分離されているため、チーム内の複数の開発者がそれぞれ担当するサーバーで独立して開発を進めることができ、開発効率が向上します。
ここまでが、私が実際に MSA を経験して感じたメリットです。
もちろん、MSA 構造は サービス運用の観点から見ると「拡張性」という強力なメリットがあり、そのメリットがすべてのデメリットを補うほど強力です。
ただし、運用の観点で書くと内容が長くなりすぎること、そして私の知識がまだ十分でないことから、今回は アプリケーション開発の視点でまとめることにしました。
デメリット 1. トランザクション管理
実装中で、MSA は単にドメインごとにサーバーを分割すれば解決するものではないということを実感しました。
最初に直面した課題は、トランザクション管理です。
トランザクション(Transaction)とは、簡単に言うと 特定の機能を実行するための一連の処理の単位です。
例えば、MSA 構造で構築された銀行アプリケーションにおいて、A というユーザーが B というユーザーに 1 万円を送金するケースを考えてみます。
この場合、実行されるべき処理の流れは以下のようになります。
- A ユーザーが 送金サーバー にリクエストを送る
- 送金サーバーは 口座サーバー に「A → B の口座へ 1 万円の送金」をリクエストする
- 口座サーバーは A の口座から 1 万円を出金し、B の口座に 1 万円を入金 し、その結果を送金サーバーに返す
- 送金サーバーは 口座サーバーのレスポンスを受け取り、ログを記録した後、A ユーザーに「送金成功」のレスポンスを返す。
この処理の流れを図で表すと、次のようになります。
しかし、もし 4 番目の処理(送金サーバーでのログ記録)でエラーが発生したら?
この場合、3 番目の処理(口座サーバーでの入出金処理)をロールバックする必要があります。
つまり、A の口座と B の口座の状態を元に戻さなければなりません。
モノリシック構造であれば、単一のトランザクションで管理されるため、この問題は比較的簡単に解決できます。
すべての処理が 1 つのデータベース内で実行され、エラーが発生すればトランザクションのロールバック機能によって自動的に処理が取り消されるからです。
しかし、MSA 構造では送金サーバーと口座サーバーは別々のデータベースを使用し、トランザクションも独立して管理されます。
そのため、単純なロールバックの仕組みを適用することができません。
この問題を解決するために、SAGA パターンの一種である「補償トランザクション」という手法を使います。
補償トランザクションとは?
補償トランザクションとは、通常のトランザクションを単にロールバックするのではなく、キャンセル処理のための新しい処理を実行する仕組みです。
今回のケースでは、A の口座に 1 万円を入金し、B の口座から 1 万円を出金するリクエストを再度送ることで、最終的にロールバックしたのと同じ状態にすることができます。
もちろん、この仕組みを導入するには追加のコード実装が必要になります。
また、開発の際には以下の点を慎重に考慮しなければなりません。
-
どのタイミングで補償トランザクションを実行するか?
- 送金サーバーのどの処理が失敗した場合に補償トランザクションを適用するのか?
-
他のサーバーを呼び出す順番をどう設計するか?
- 不要な補償トランザクションの発生を最小限に抑えるにはどうすればよいか?
このように、MSA ではトランザクション管理がより複雑になり、開発者の負担が増える というデメリットがあります。
デメリット 2. 他のサーバーの DB データが必要な場合、JOIN を使用できない
もちろん、モノリシック構造でも 必要に応じてドメインごとに DB を分割することがありますが、
この場合でも 異なるサーバーで管理されているデータベース間では JOIN を使用することができません。
例えば、以下のようなテーブル構造があるとします。
この場合、次のようなクエリは問題なく動作します。
SELECT A.BLOG_CONTENT,
B.USER_NICKNAME
FROM BLOG_TABLE AS A
LEFT JOIN USER_TABLE AS B ON A.USER_ID = B.USER_ID
WHERE A.BLOG_PK = '1234'
しかし、 USER_TABLE と BLOG_TABLE が 異なるサーバーのデータベースに存在している場合、上記のような JOIN クエリを実行することはできません。
そのため、BLOG サーバーは USER_NICKNAME を取得するために USER サーバーへリクエストを送り、そのレスポンスを受け取った後、アプリケーション側でデータをマッピングする必要があります。
当然ですが、このプロセスは モノリシック構造と比較すると追加の Network I/O が発生するため、パフォーマンスの面で不利になるという問題があります。
ここまでがアプリケーション開発の視点で感じた MSA のメリットとデメリットでした。
また、MSA ではデバッグが難しくなる というデメリットもありますが、これまで説明した内容と重複する部分が多いため、今回は省略しました。
今回の記事では アプリケーション開発の視点から MSA のメリットとデメリット を整理しましたが、実際には MSA の長所と短所は、インフラの観点から見るとより顕著に現れると考えています。
最後まで読んでいただき、ありがとうございました。