ひとことで言うと#
単一のデプロイユニット(モノリス)を維持しつつ、内部をドメインごとのモジュールに分割し、モジュール間の依存を明示的なインターフェースで制御するアーキテクチャパターン。
押さえておきたい用語#
- Modular Monolith(モジュラー モノリス)
- 単一デプロイでありながら内部がモジュール単位で分離されたアーキテクチャを指す。
- Module Boundary(モジュール境界)
- モジュール間の依存を制御する境界線を指す。パッケージ・名前空間・プロジェクト分割で実現する。
- Public API(パブリック API)
- モジュールが外部に公開する唯一のアクセスポイント。内部実装を隠蔽するためのインターフェース。
- Big Ball of Mud(ビッグ ボール オブ マッド)
- モジュール境界がない無秩序なモノリス。どこからでも何でも参照できる状態である。
- Internal Event(インターナル イベント)
- モジュール間を疎結合に連携させるためのイベント通知。直接呼び出しを避ける手法。
モジュラーモノリスの全体像#
こんな悩みに効く#
- モノリスが無秩序に育ってしまい、どこを変更しても想定外の箇所に影響が出る
- マイクロサービスに移行したいが、分散システムの複雑さを扱えるチーム体制ではない
- モジュール境界を決めたいが、どこで切ればいいかわからない
基本の使い方#
具体例#
エンジニア50名のEC企業。10年かけて育った30万行のモノリスは、注文処理のコードから在庫テーブルを直接参照するなど依存が絡み合い、1機能の修正に平均 4チーム の調整が必要だった。
6つのモジュール(注文・在庫・ユーザー・決済・配送・商品)に分離。各モジュールにPublic APIを定義し、CIでモジュール境界違反を自動検出する仕組みを導入。
リファクタリングに6ヶ月かけた結果、1機能の修正に必要なチーム数が 4 → 1.2 に減少。デプロイ起因のバグが 月12件 → 月3件 に減った。
エンジニア8名のシリーズAスタートアップ。「将来はマイクロサービスにする」前提で、最初からモジュラーモノリスで設計。
3モジュール(テナント管理・課金・コア機能)でスタートし、各モジュールはGoのinternal packageで境界を実現。モジュール間はインターフェース経由でのみ通信。
シリーズBで20名に拡大した時点でも、モジュール境界が明確なためチーム分割がスムーズ。各チームが独立してモジュールを開発できる体制になるまで 2週間 で移行完了。マイクロサービス化は規模が50名を超えてからの判断に先送りした。
エンジニア30名の証券系スタートアップ。「モダンにやろう」と初期から12個のマイクロサービスで構築したが、分散トランザクションの複雑さ・サービス間通信の遅延・運用コストが許容範囲を超えた。
12サービスを4モジュールのモジュラーモノリスに統合。インフラコストが 月額180万円 → 65万円 に削減。分散トランザクションが不要になったことで、決済処理の整合性バグがゼロに。デプロイパイプラインも12本→1本に集約され、運用負荷が激減した。
やりがちな失敗パターン#
- モジュール境界を決めてもCIで強制しない — ルールだけ作っても、忙しいときに「ちょっとだけ直接参照」が発生し、半年で境界が崩壊する。自動検出を必ず入れる
- モジュールを細かく分けすぎる — 3〜8モジュール程度が管理しやすい。20モジュールは分割しすぎで、マイクロサービスと同じ複雑さが生まれる
- 共有データベースを1つのスキーマで使う — モジュールごとにスキーマを分離するか、少なくともテーブルの所有モジュールを明確にする
- 「いずれマイクロサービスに」を理由に過剰設計する — モジュラーモノリスで十分な規模なのに、メッセージキューやサービスディスカバリを導入する必要はない
まとめ#
モジュラーモノリスは単一デプロイの利便性を保ちながら、内部をドメインごとのモジュールに分離して秩序を維持するアーキテクチャパターン。モジュール間はPublic APIのみで通信し、内部実装を隠蔽するルールをCIで自動的に強制する。マイクロサービスの分散システム複雑性を回避しつつ、将来の分離に備えた境界設計を実現できるため、多くのチームにとって現実的な選択肢になる。