目次

  1. マイクロサービス化の前に考えること
  2. サービス境界の設計
  3. 段階的な移行戦略
  4. データ管理
  5. サービス間通信
  6. 耐障害性
  7. 可観測性(オブザーバビリティ)
  8. デプロイとインフラ
  9. セキュリティ
  10. テスト戦略
  11. 組織とチーム
  12. よくあるアンチパターン
  13. 移行チェックリスト

1. マイクロサービス化の前に考えること

本当にマイクロサービスが必要か

マイクロサービスは「スケールの問題」と「組織の問題」を解くための手段であり、それ自体が目的ではありません。導入すると分散システム特有の複雑さ(ネットワーク障害、データ整合性、運用負荷)を抱えることになります。

マイクロサービスが向いているケース

  • 複数チームが同じコードベースで作業し、リリースが互いにブロックし合っている
  • 機能ごとにスケール要件が大きく異なる(例: 検索だけ負荷が桁違い)
  • 一部の機能だけ異なる技術スタックやリリース頻度が必要
  • ドメインがある程度安定しており、境界が見えている

まだ早いケース

  • チームが小さい(目安として1〜2チーム程度)
  • プロダクトの方向性が固まっておらず、ドメインが頻繁に変わる
  • CI/CD、監視、コンテナ基盤などの運用基盤が未整備
  • 現状の課題が「コードが汚い」だけ(分割しても汚い分散システムになるだけ)

モジュラーモノリスという選択肢

いきなり分割する前に、モノリス内部をモジュール単位で明確に分離する「モジュラーモノリス」を経由するのが安全です。

  • モジュール間の依存を公開インターフェース経由に限定する
  • モジュールごとにDBスキーマ(テーブル群)の所有者を決める
  • 境界が正しいと確認できたモジュールから順に切り出す

境界の誤りはモノリス内なら低コストで直せますが、サービス分割後に直すのは非常に高コストです。


2. サービス境界の設計

ドメイン駆動設計(DDD)で境界を決める

  • 境界づけられたコンテキスト(Bounded Context) を単位にサービスを切る
  • 技術レイヤー(UIサービス、DBサービスなど)ではなく、ビジネス能力(注文、在庫、決済、配送など)で分ける
  • イベントストーミングなどのワークショップでドメインエキスパートと一緒に境界を洗い出す

良い境界の判断基準

観点良い状態
凝集度一緒に変更されるものが同じサービスにある
結合度1つの機能追加で複数サービスの同時変更がほぼ不要
自律性他サービスが落ちていても主要機能が動く
データ所有データの「正」の持ち主が1サービスに決まっている
チーム1サービスを1チームが責任を持って運用できる

サイズの目安

「何行以下」といった基準はあまり意味がありません。目安は以下です。

  • 1チーム(5〜9人程度)が理解・運用しきれる大きさ
  • 独立してデプロイしても他に影響が出ない単位
  • 細かくしすぎると通信コストと運用負荷が爆発する(ナノサービス化に注意)

3. 段階的な移行戦略

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

一括書き換え(ビッグバンリライト)は避け、既存システムを少しずつ置き換えます。

  1. モノリスの前段にプロキシ / APIゲートウェイを置く
  2. 切り出す機能を新サービスとして実装する
  3. ルーティングを新サービスに段階的に切り替える
  4. 旧実装を削除する
  5. 次の機能で繰り返す

切り出す順番

  • 最初は: 依存が少なく、変更頻度が高く、失敗しても影響が小さい機能
  • 後回し: ドメインの中核で依存が多い機能(境界の理解が深まってから)
  • ビジネス価値(スケール・リリース速度の改善)が明確なものを優先する

移行中の注意点

  • 新旧並行稼働期間は、フィーチャーフラグやカナリアリリースで切り替える
  • 新旧の結果を比較する「シャドーイング(ダークローンチ)」で正しさを検証する
  • 移行の完了条件(旧コードの削除)まで計画に含める。並行稼働の放置は負債になる

4. データ管理

Database per Service

  • 各サービスが自分のデータストアを所有し、他サービスのDBに直接アクセスしない
  • 他サービスのデータが必要ならAPIかイベント経由で取得する
  • 共有DBのままだとスキーマ変更が全サービスに波及し、分割の意味がなくなる

移行時のデータ分離手順(例)

  1. 同一DB内でスキーマ(テーブル群)の所有者を決める
  2. 他サービスのテーブルへの直接JOINをAPI呼び出しに置き換える
  3. 論理的に分離できたら物理的に別DBへ移す

分散トランザクションへの対処

2フェーズコミットは可用性とスケーラビリティを損なうため、基本的に避けます。

  • Sagaパターン: ローカルトランザクションの連鎖で処理し、失敗時は補償トランザクションで取り消す
    • コレオグラフィ型: 各サービスがイベントに反応して次の処理を行う(単純なフロー向け)
    • オーケストレーション型: オーケストレーターが全体を制御する(複雑なフロー向け、可視性が高い)
  • Transactional Outboxパターン: DB更新とイベント発行を同一トランザクションで確実に行う
  • 結果整合性を前提に、ビジネス側と「どこまでの遅延を許容するか」を合意しておく

その他

  • CQRS: 読み取りと書き込みのモデルを分離し、参照用に他サービスのデータを非正規化して持つ
  • べき等性: メッセージの重複配信を前提に、同じ処理を複数回受けても結果が変わらないよう設計する

5. サービス間通信

同期 vs 非同期

方式例向いている用途注意点
同期REST, gRPC即時に結果が必要な問い合わせ呼び出し先の障害が伝播しやすい
非同期Kafka, RabbitMQ, SQS など状態変更の通知、後続処理のトリガー順序保証・重複・デバッグの難しさ
  • 状態変更の伝播は非同期イベントを基本にすると結合度が下がる
  • 同期呼び出しの連鎖(A→B→C→D)は可用性を掛け算で下げるため避ける

API設計

  • 契約を明確にする: OpenAPI / Protocol Buffers などでスキーマを定義し、コードより先に合意する
  • 後方互換性を守る: フィールドの追加はOK、削除・型変更は新バージョンで行う
  • バージョニング: URLやヘッダーでバージョンを管理し、旧バージョンの廃止期限を周知する
  • トレラントリーダー: 受信側は知らないフィールドを無視し、必要な項目だけ読む

APIゲートウェイとBFF

  • 外部クライアントからの入口はAPIゲートウェイに集約し、認証・レート制限・ルーティングを担わせる
  • クライアント種別(Web、モバイルなど)ごとに BFF(Backend for Frontend) を置くと、クライアント都合のAPI変更がバックエンドに波及しにくい
  • ゲートウェイにビジネスロジックを入れすぎない

6. 耐障害性

分散システムでは「障害は必ず起きる」前提で設計します。

  • タイムアウト: すべての外部呼び出しに設定する(デフォルト無制限は厳禁)
  • リトライ: 指数バックオフ+ジッターで行い、べき等な操作に限定する
  • サーキットブレーカー: 失敗が続く呼び出し先を一時的に遮断し、障害の連鎖を防ぐ
  • バルクヘッド: 呼び出し先ごとにスレッドプールや接続数を分け、1つの障害が全体を巻き込まないようにする
  • フォールバック / グレースフルデグラデーション: キャッシュ値やデフォルト値で機能を縮退させて提供する
  • レート制限・負荷制御: 過負荷時はリクエストを早めに拒否し、システム全体の崩壊を防ぐ
  • カオスエンジニアリング: 本番相当の環境で意図的に障害を注入し、耐性を検証する

7. 可観測性(オブザーバビリティ)

サービスが増えると「どこで何が起きているか」が分からなくなります。分割前に整備しておくべき最重要項目です。

3本柱

  • ログ: 構造化ログ(JSON等)で出力し、集中管理基盤に集約する
  • メトリクス: RED(Rate / Errors / Duration)やUSE(Utilization / Saturation / Errors)を各サービスで計測する
  • 分散トレーシング: リクエストIDをサービス間で伝播し、1リクエストの全経路を追跡できるようにする(OpenTelemetryが事実上の標準)

運用面

  • 相関ID: すべてのログ・トレースに共通のリクエストIDを付与する
  • SLI / SLO: サービスごとに目標値を定義し、エラーバジェットでリリース判断を行う
  • アラート: 原因ではなく症状(ユーザー影響)ベースで設定し、アラート疲れを防ぐ
  • ヘルスチェック: liveness(生きているか)と readiness(リクエストを受けられるか)を分けて実装する
  • サービスカタログ: 各サービスのオーナー、依存関係、ドキュメント、Runbookを一元管理する

8. デプロイとインフラ

  • 独立デプロイ: 各サービスを他と調整せずにリリースできることがマイクロサービスの前提条件
  • CI/CDパイプライン: サービスごとにビルド・テスト・デプロイを自動化する
  • コンテナ化とオーケストレーション: Docker+Kubernetesなどで実行環境を標準化する
  • Infrastructure as Code: Terraform等でインフラをコード管理し、環境差異をなくす
  • 安全なリリース手法: カナリアリリース、ブルーグリーンデプロイ、フィーチャーフラグを使い分ける
  • ゴールデンパス / プラットフォームエンジニアリング: サービスのテンプレート(ログ、監視、CI設定込み)を用意し、新サービス立ち上げのコストを下げる
  • 設定とシークレット: 環境ごとの設定はコードから分離し、シークレットは専用の管理基盤(Vault、クラウドのSecret Managerなど)で扱う
  • サービスメッシュ: サービス数が多くなったら、mTLS・リトライ・トラフィック制御をIstio / Linkerd等で共通化することも検討する(導入・運用コストとのトレードオフ)

9. セキュリティ

  • ゼロトラスト: 内部ネットワークだからといって信頼しない。サービス間通信もmTLSなどで認証・暗号化する
  • 認証・認可:
    • 外部からの認証はゲートウェイ(またはIdP)で集中して行う
    • ユーザー情報はJWT等のトークンで伝播し、各サービスで認可を判断する
    • サービス間の呼び出し権限も最小権限で定義する
  • シークレット管理: コードやイメージにシークレットを埋め込まない
  • サプライチェーン対策: 依存ライブラリやコンテナイメージの脆弱性スキャンをCIに組み込む
  • 監査ログ: 誰がいつ何をしたかを追跡できるようにする

10. テスト戦略

  • ユニットテスト: 各サービス内部のロジックを高速に検証する(土台として最も厚く)
  • コンポーネントテスト: サービス単体を、依存先をモック/スタブにして検証する
  • コントラクトテスト: Pact等で提供側と利用側のAPI契約を検証し、互換性破壊をCIで検知する。E2Eテストの多くを置き換えられる
  • E2Eテスト: 重要なユーザーフローに絞って最小限にする(遅く壊れやすいため増やしすぎない)
  • 本番でのテスト: カナリア、合成監視、カオスエンジニアリングで本番環境の健全性を継続的に確認する

11. 組織とチーム

  • コンウェイの法則: システム構造は組織のコミュニケーション構造を反映する。サービス境界とチーム境界を一致させる(逆コンウェイ戦略)
  • You build it, you run it: サービスを作ったチームが運用・オンコールまで責任を持つ
  • チームの認知負荷: 1チームが抱えるサービス数を管理可能な範囲に抑える
  • チームトポロジー: ストリームアラインドチームを中心に、プラットフォームチームが共通基盤を提供し、イネーブリングチームが技術支援する体制が有効
  • 標準化と自由のバランス: 言語・フレームワークの自由度は認めつつ、ログ形式・監視・API規約・セキュリティ要件などの「横串」は標準化する
  • ドキュメント: ADR(Architecture Decision Records)で設計判断の理由を残す

12. よくあるアンチパターン

アンチパターン症状対策
分散モノリス1機能の変更で複数サービスの同時デプロイが必要境界を見直し、非同期化・データ所有の明確化を行う
共有データベース複数サービスが同じテーブルを読み書きするDatabase per Serviceへ段階移行する
ナノサービスサービスが細かすぎて通信と運用が過剰関連するサービスを統合する
同期呼び出しの連鎖1つの障害が全体に伝播、レイテンシ悪化非同期イベント化、データのキャッシュ・複製
共通ライブラリの肥大化共有ライブラリの更新で全サービスの改修が必要共有はロギング等の横断的関心事に限定し、ドメインロジックは共有しない
技術先行の分割Kubernetesを入れたい、が動機になっているビジネス・組織課題から出発する
可観測性の後回し障害時に原因箇所が特定できない分割前にログ・メトリクス・トレースを整備する
ビッグバン移行全面書き換えが長期化・頓挫するストラングラーフィグで段階移行する

13. 移行チェックリスト

着手前

  • マイクロサービス化で解決したい課題(ビジネス・組織)が明文化されている
  • モジュラーモノリスなど、より軽い代替案を検討した
  • ドメイン分析を行い、サービス境界の候補がある
  • CI/CDパイプラインが整備されている
  • ログ集約・メトリクス・分散トレーシングの基盤がある
  • コンテナ基盤・IaCが用意されている

サービスごと

  • オーナーチームが明確
  • データの所有権が明確で、他サービスのDBに直接アクセスしていない
  • API契約(スキーマ)が定義され、バージョニング方針がある
  • タイムアウト・リトライ・サーキットブレーカーが実装されている
  • ヘルスチェック(liveness / readiness)がある
  • SLOとアラートが設定されている
  • コントラクトテストがCIで実行されている
  • Runbook(障害対応手順)がある
  • 独立してデプロイ・ロールバックできる

移行完了時

  • 旧実装(モノリス側のコード・テーブル)が削除されている
  • 移行前に設定した課題が改善したかを計測・振り返りした

0件のページ