マイクロサービス設計パターン実践ガイド
マイクロサービス設計パターン実践ガイド
はじめに:モノリスの地獄からの脱出
「この部分を変更したいだけなのに、なんでシステム全体をビルドしなきゃいけないの...」 「デプロイに3時間もかかるのか...」 「一部の障害で全サービスが止まった!」
大規模なモノリシックアプリケーションを運用していた時の悩みです。コードベースは100万行超え、ビルド時間は30分...開発チームの士気は下がる一方でした。
そんな時にマイクロサービスアーキテクチャに出会いました。最初は「本当にうまくいくの?」と半信半疑でしたが、実際に導入してみると世界が変わりました。
今回は、大規模プロジェクトでマイクロサービスを成功させるために使った設計パターンを、実体験を交えてお話しします。
成功を左右した設計パターン
API Gatewayで地獄から天国へ
最初の悩みは「クライアントが各サービスのURLを全部知らなきゃいけない」というものでした。
- ユーザーサービス:https://user-service.company.com
- 注文サービス:https://order-service.company.com
- 在庫サービス:https://inventory-service.company.com
フロントエンドチームから「もう無理!」と悲鳴が上がりました。
API Gatewayを導入したら:
- 全てのAPIが1つのURLからアクセス可能に
- 認証・認可も1箇所で管理
- レート制限も簡単に設定
「これだよ、これが欲しかった!」とフロントエンドチームは大喜びでした。
Circuit Breakerで障害の連鎖を防いだ話
在庫確認サービスが落ちて、全体が機能停止...
午前3時、アラートで叩き起こされました。「また全サービスダウン?」 調べてみると、在庫確認サービスの応答が遅くなり、それを待つ注文サービスがタイムアウト、さらにそれを待つフロントエンドも... まるでドミノ倒しのように全体が崩壊していました。
そこで導入したのがCircuit Breakerパターン:
- 3回失敗したら、30秒間は自動的に「使用停止」に
- その間は別の手段(キャッシュなど)で対応
- 30秒後に再度チャレンジ
「ブレーカーが落ちる」みたいに、問題のあるサービスを一時的に切り離すんです。
導入後の効果:
- 障害が他のサービスに波及しなくなった
- 復旧時間が1/10に短縮
- 夜中に起こされることが激減(これが一番嬉しい!)
Sagaパターンで分散トランザクションの悪夢を解決
「決済は成功したのに在庫が確保されてない!」
マイクロサービスあるあるですよね。モノリスの時は簡単だったトランザクション管理が、分散システムでは地獄に...
実際に起きた問題:
- 在庫確保 → 成功
- 決済処理 → 成功
- 配送手配 → 失敗!
結果:お客様のお金は引き落とされたのに、商品が送れない...
Sagaパターンを導入したら:
- 各ステップで「元に戻す方法」を記録
- どこかで失敗したら、逆順に取り消し処理
- 「決済をキャンセル」→「在庫を戻す」のように
導入後の成果:
- 不整合によるクレームがゼロに
- システムエラー時も自動でロールバック
- お客様への返金も自動化
つまずきポイント: 最初は「補償処理も失敗したらどうする?」で悩みました。結論:ログに記録して、後で手動対応できるようにしました。完璧を求めすぎないのも大切です。
Event Sourcingで「いつ何が起きたか」完全把握
「このデータ、いつ誰が変更したの?」
カスタマーサポートから毎日こんな質問が... データベースの現在の状態しか分からないと、過去の経緯が追えないんですよね。
Event Sourcingを導入して世界が変わりました:
- すべての変更を「イベント」として記録
- 「注文作成」「ステータス変更」「キャンセル」など
- タイムマシンのように過去の状態を再現可能
実際の威力を実感した瞬間:
お客様:「先週の火曜日、注文をキャンセルしたはずなのに請求が来た」
従来:「えーっと...ログを掘って...DBのバックアップ見て...」(2時間)
Event Sourcing後:イベント履歴を見れば一目瞭然!(30秒)
- 14:23 注文作成
- 14:45 決済処理
- 14:46 キャンセルリクエスト
- 14:47 システムエラーでキャンセル失敗
原因も対応もすぐに分かるようになりました。
嬉しい副産物:
- 監査対応が楽になった(全履歴が残ってる)
- バグの原因特定が高速化
- 「なぜこうなった?」がすぐ分かる
サービス間通信で学んだ教訓
同期通信(REST)の落とし穴と対策
「ユーザーサービスが遅いせいで全体が遅い!」
マイクロサービス化して最初にぶつかった壁です。 A→B→Cとサービスを呼ぶと、レスポンスタイムが足し算に...
失敗談: 最初は愚直にHTTPで通信していました:
- タイムアウトなし → 永遠に待ち続ける
- リトライなし → 一時的なエラーで即失敗
- エラーハンドリングなし → 500エラーの嵐
改善後の結果:
// シンプルな例
services.AddHttpClient<IUserServiceClient>()
.AddPolicyHandler(GetRetryPolicy()) // 3回までリトライ
.AddPolicyHandler(GetCircuitBreakerPolicy()); // 障害時は即座に失敗
これだけで:
- 一時的なネットワークエラーの90%が解消
- レスポンスタイムが平均200ms短縮
- エラー率が5%から0.1%に改善
非同期通信で処理速度10倍に!
「注文確定に30秒かかるんですけど...」
お客様からのクレームでした。調査すると:
- 在庫確認(5秒)
- 決済処理(10秒)
- メール送信(8秒)
- 配送手配(7秒)
全部直列で処理してたんです。お客様を30秒も待たせるなんて...
メッセージキューで革命が起きた:
注文確定ボタンを押したら:
- 即座に「注文受付完了」を返す(0.1秒)
- 裏で各処理を並行実行
- 完了したらメールでお知らせ
結果:体感速度が300倍向上!
実装のコツ:
- 重要度で処理を分ける(決済は最優先)
- 失敗してもリトライする仕組み
- 処理状況を追跡できるように
失敗談: 最初はメッセージが消えることがありました。原因は処理完了前にACKしていたこと。「処理が終わってから完了通知」という基本を守らないと大変なことに...
運用で命を救われた監視の仕組み
分散トレーシングがなかったら廃業してた話
「なんか最近システム遅くない?」
漠然とした不満が聞こえてきた時、以前なら途方に暮れていました。 どのサービスが原因?どの処理が遅い?全く分からない...
分散トレーシングを導入して、探偵になれました:
ある日の調査: 注文処理が遅いというクレーム ↓ トレースを確認
- API Gateway: 10ms ✓
- 注文サービス: 50ms ✓
- 在庫サービス: 3,000ms ← 犯人発見! ↓ さらに詳細を確認
- DB接続: 2,900ms ← インデックスが壊れてた!
30分で原因特定、1時間で解決。以前なら3日かかってました。
導入のポイント:
// 最小限の設定で始められる
services.AddOpenTelemetryTracing(builder =>
builder.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddJaegerExporter());
嬉しい発見:
- ボトルネックが一目瞭然
- 無駄な処理を次々発見(30%高速化)
- 障害対応が「勘」から「データ」に
ヘルスチェックで未然に防いだ大障害
金曜日の夕方、アラートが鳴りました。 「注文処理サービスのヘルスチェック:Degraded」
確認すると、未処理注文が1000件を超えていました。 まだお客様からのクレームは来ていませんが、このままだと...
ヘルスチェックの階層:
-
Live(生きてる?)
- プロセスが動いてるかだけチェック
- Kubernetesが再起動判断に使用
-
Ready(仕事できる?)
- DB接続OK?
- 外部API繋がる?
- 初期化完了?
-
カスタムチェック(調子どう?)
- 未処理タスクの数は?
- レスポンスタイムは正常?
- エラー率は許容範囲?
実装例(最小限から始める):
// まずはシンプルに
app.MapHealthChecks("/health");
// 徐々に賢く
services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>()
.AddCheck("OrderBacklog", () =>
pendingOrders < 1000 ? Healthy : Degraded);
導入効果:
- 障害を事前に検知(お客様より先に気づく)
- 自動復旧の精度向上
- 「なんとなく調子悪い」を数値化
セキュリティで冷や汗をかいた話
認証・認可の落とし穴
「内部APIだから認証いらないよね」
...大間違いでした。
ある日、ネットワーク設定をミスって、内部APIが外部公開されてしまいました。 幸い、すぐに気づいて事なきを得ましたが、もし悪用されていたら...
学んだ教訓:すべてのAPIに認証を
JWT(JSON Web Token)を使った認証を全サービスに実装:
- 各リクエストにトークンを付与
- 有効期限は短めに(15分)
- リフレッシュトークンで更新
実装のコツ:
// 最小限の設定例
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true
// 設定値は環境変数から
};
});
つまずきポイント:
- トークンの有効期限が長すぎて、退職者がアクセスできた
- 内部通信用のトークンが漏洩しやすかった
- エラーメッセージで情報が漏れた("User not found"はNG)
今では「ゼロトラスト」が合言葉。社内ネットワークでも一切信用しません。
まとめ:マイクロサービスは銀の弾丸じゃないけど、確実に世界を変えた
3年前、100万行のモノリスに苦しんでいた私たちが、今では50個のマイクロサービスを運用しています。
正直に言います。楽ではありませんでした。
苦労したこと:
- 分散システムの複雑性に何度も泣いた
- 最初のサービス分割で大失敗(細かすぎた)
- トランザクション管理で頭を抱えた
- 監視ツールなしでは何も分からなかった
でも、得たものは計り知れません:
- デプロイが怖くなくなった(1日10回以上デプロイ)
- 障害の影響範囲が限定的に
- チームが自律的に動けるように
- 新機能追加が10倍速く
一番の収穫: 「このシステム、誰か全体を理解してる?」という不安から解放されました。 各サービスは小さく、新人でも1週間で理解できます。
これから始める方へ:
- 小さく始めましょう(最初は2-3サービスから)
- 監視は最初から入れましょう(後悔します)
- 完璧を求めすぎない(70点でもモノリスよりマシ)
- チームの文化も変える必要があります
マイクロサービスは魔法の杖ではありません。 でも、正しく使えば、確実にエンジニアリングを楽しくしてくれます。
「ビルドに30分」「デプロイが怖い」「コードが複雑すぎて触れない」
そんな悩みから解放されて、本来のものづくりの楽しさを取り戻せました。
エンハンスド株式会社では、マイクロサービス化の旅路を一緒に歩むパートナーとして、戦略立案から実装、そして運用まで、実体験に基づいた支援を提供しています。
「うちもマイクロサービス化したいけど、どこから始めれば...」という方、ぜひお気軽にご相談ください。失敗も成功も、すべて共有します。
参考リンク
- Martin Fowler - Microservices
- .NET Microservices Architecture Guidance
- Azure でのマイクロサービス
- Microservices.io
- CNCF Cloud Native Interactive Landscape
関連サービス:
執筆者: エンハンスド株式会社 アーキテクチャチーム
公開日: 2025年4月2日
カテゴリ: アーキテクチャ, マイクロサービス
タグ: #マイクロサービス #設計パターン #分散システム #クラウドネイティブ