「モジュラーモノリス」のモジュール性について

こんなつぶやきをしたので、「モジュラーモノリス」について考える。特にモジュール性について考えてみる。

以下の主張は、実際のプロジェクト/プロダクトにとって最適かどうかは、具体的なビジネス要件、チームのスキルセット、運用上の制約など、多くの要因に依存する。アーキテクチャは常にトレードオフがあり、一つの解決策が全てのシナリオに適用できるわけではないので、その点を考慮して読んでほしい。

マイクロサービスではなくモジュラーモノリスを選択した場合には、しばしば以下の利点が強調される。

  1. ネットワーク通信: モジュラーモノリスは、マイクロサービスと異なり、ネットワークを介した通信が発生しないため、ネットワーク遅延や断絶、通信エラーなどの問題が生じにくくなる

  2. データ整合性: 分散システムでは、データの整合性を保つために複雑なトランザクション管理やデータ同期が必要になるが、モジュラーモノリスでは同じデータベース内での操作が多く、これらの問題が少なくなる

  3. デプロイメント: マイクロサービスでは各サービスを個別にデプロイし管理する必要があるが、モジュラーモノリスでは一つのアプリケーションとしてデプロイするため、オペレーションが単純化される

  4. 開発とデバッグ: ローカルでの開発やデバッグがマイクロサービスに比べて容易。マイクロサービスの場合、分散システムの各コンポーネント間で発生する問題を追跡することがより困難

しかし、1,2の点については、軽減されるものの注意が必要である。コンテキスト境界を明確に維持するためには、モジュール間インターフェースの設計に細心の注意を払うべきである。コンテキスト間での直接的な実装依存を避け、インターフェースのみに依存することが必須である。

コンテキスト間のネットワーク通信は物理的にはなくなるが、例えば、コンテキストAからコンテキストBへのAPI呼び出しは、メソッドコールに置き換えられる可能性がある(補足: コンテキストを跨ぐ処理が同じスレッドになっていいのか問題がある…。コンピュータ資源もコンテキストごとに分離するならアクターのようなメッセージ駆動のほうが都合がいい) 。しかし、秩序が保たれなければ、簡単に密結合の泥団子へと進むリスクがある。そのため、同一システム内でもあえてAPIを呼び出す選択肢もあり得る。Event Busを介したイベント駆動アーキテクチャを採用する場合も、イベントの一貫性、順序付け、配信保証の問題を考慮する必要がある。

データ整合性に関しても、モジュール性を考慮してコンテキスト境界ごとにトランザクションを扱うのであれば、マイクロサービスであろうがモジュラーモノリスであろうが複雑さに大差はない。便利だからと同じトランザクションにまとめるとそこから密結合が始まる。

つまり、モジュール性を考慮する部分では、設計上の注意が必要であり、ある程度の複雑さは避けられない。「あとから分けられるようにモジュラーモノリスにしよう」といいつつ、上記の観点を考慮しないとそんなことは難しいだろう。このことを考慮すると、以下の図のような概念も考えられる。

  • 外側のオレンジの矩形はシステム境界を示し、同じ枠内のオレンジ色に塗りつぶされた矩形はコンテキスト境界を表す。マイクロサービスではシステムに相当するが、モジュラーモノリスでは単体アプリケーション内部のトップレベルモジュールである。

  • 小さな●は外部公開するAPIを示し、クライアントからコールされる。

  • コンテキスト間は同じアプリケーション内にあっても、実装への直接の依存はできない。自コンテキストから他コンテキストのデータを参照する場合は、内部APIまたは抽象的なインターフェースを経由して利用する。

  • データベースは物理的に同じかどうかは要件に依存するが、コンテキストごとに論理的なスキーマは分けられる。

  • コンテキスト間でドメインイベントをPub/Subする場合はEvent Busを利用する。このEvent BusがKafka/Kinesisのような外部システムであるか、ランタイム上のコールバックであるかは要件次第である。

---

コンテキスト境界を意識してモジュール性を考える際、ネットワーク通信やデータの整合性の問題は完全には解消されない。これらの点を想起しながら設計に取り組まなければ、本質的な意味でモジュラーモノリスの実現は難しいだろう。

@j5ik2o
歳をとっても霜降り肉!をモットーにしております