要点: マイクロサービスは大規模なコードベースに対する勲章ではなく、運用と障害のモデルです。
課題
技術レイヤーで分割すると、すべてのリクエストがサービスを跨ぎ、リリース調整、分散トランザクション、ネットワーク障害によるコストが価値よりも先に増大します。
単純な対策だけでは不十分な理由
コードの規模だけで分割を判断すべきではありません。高い変更結合度、異なる可用性要件、セキュリティの隔離、あるいは独立したスケール要件が強い根拠となります。
モジュラーモノリスはプロセス内の高速なトランザクションを維持しつつ、組織が所有権を尊重できるかを検証します。プロセス内で境界を維持できない場合、ネットワークを挟んでも設計は改善されません。
処理フロー
所有権と不変条件→モジュラーモノリス
境界の強制→抽出ポイント
契約とデータ→マイクロサービス
独立運用
アーキテクチャ上の判断
ビジネス機能による分割
同一の不変条件を保護するルールとデータを1か所に保持します。ユーザーサービスやDBサービスといった技術レイヤーでの分割は避けます。
同期呼び出しの限定
呼び出し元が即座に結果を必要とする場合にのみ同期処理を使用し、それ以外はイベントによる非同期反応を採用します。
運用ライフサイクルの所有
サービスの抽出には、単にコードを移動するだけでなく、オンコール体制、ダッシュボード、デプロイ、セキュリティ、キャパシティ管理の所有が含まれます。
さらに深く考える
データ所有権こそが最も堅牢な境界
サービスはリードモデルやイベント、APIを公開できますが、他のサービスがそのテーブルへ直接書き込むことは禁止されます。一時的な複製には所有者と削除期日を設定します。
ストラングラー移行におけるデータ照合
移行期間中は新旧の実行結果を比較照合し、少数のコホート(ユーザー群)から段階的に切替え、データ同等性が確認されるまで迅速な切り戻しルートを維持します。
実装手順
- ビジネスの機能、不変条件、変更の所有者をマッピングします。
- モノリス内でモジュールAPIと依存関係ルールを強固に適用します。
- 契約、データ移行、およびロールバック経路を整えて1つの機能を抽出します。
技術例: 抽出前の依存関係ルール定義
module Orders exposes OrdersApi
module Billing exposes BillingApi
Orders may call BillingApi
Orders must not import BillingRepository
architectureTest.assertNoInternalDependencyLeaks()自動化されたアーキテクチャテストにより、意図したモジュールグラフをビルド時の制約として適用します。
想定すべき障害パターン
- 共有データベースにより、サービスが契約をバイパスしてしまう。
- 細かすぎる境界定義により、分散N+1問題が発生する。
- 2つのチームが同一の不変条件を所有していると誤認する。
監視すべきシグナル
| シグナル | 確認する理由 |
|---|---|
| モジュール間変更頻度 | 一括リリースを必要とする依存関係が残存しているかを示します。 |
| 1ジャーニーあたりのリモート呼び出し数 | 過度な細粒度化とそれによる遅延の増加を示します。 |
| デプロイおよびインシデントの独立性 | 抽出によって真の自律性が得られたかを検証します。 |
設計の検証方法
- CI環境で不正な内部モジュールのインポートを遮断します。
- 抽出予定の境界に対して事前に遅延や障害を注入して検証します。
- 新旧両方のパスで同一インプットを実行し、出力結果を照合します。
安全なRollout計画
明確な所有権を持つ1つのモジュールから着手します。リードパス、コマンド、データの順で抽出し、テナントごとのルーティングフラグと切り戻し手順を維持した上で、運用の独立性が証明されてから旧コードを削除します。
本番前チェックリスト
- 各サービスが自身のデータと業務判断を完全に所有していること。
- サービス間呼び出しにレイテンシー、タイムアウト、障害予算が設定されていること。
- プラットフォーム全体で全サービスを統一的にデプロイ、監視、保護できること。
まとめ
優れた境界は調整コストを削減し、不適切な境界は単に関数呼び出しをネットワーク上に移動させるだけに終わります。
