note: モジュラーモノリスについて

モジュラーモノリスについて、人によって想定してること違くね?となったので自分なりに整理メモ

「マイクロサービスアーキテクチャ 第2版」での記載
マイクロサービスアーキテクチャ 第2版
マイクロサービス、モノリス
上記はデプロイ単位で分かれる概念。
- モノリス:全機能が同一デプロイ単位
- マイクロサービス:機能によって異なるデプロイ単位
モジュラーモノリス
同書の記述では、モジュラーモノリスはデプロイ単位が単一のためモノリスに含まれる。
モジュラーモノリスの特徴は、バックエンドのコードや提供サービスの機能単位などでモジュール化されていること。
DBの機能単位などでの分割は問わない。
同書では、元々はDB同一だったが、最近はDBも分割している事例もあるという書きぶり。

具体事例:shopfiy
「マイクロサービスアーキテクチャ 第2版」内でモジュラーモノリス事例としてshopifyが記載。
同記事内でのモジュラーモノリスの定義
A modular monolith is a system where all of the code powers a single application and there are strictly enforced boundaries between different domains.
モジュラーモノリスは、すべてのコードが単一のアプリケーションを実行し、異なるドメイン間に厳密に境界が適用されるシステムです。
モジュラーモノリスの制約
同記事のshopfiyは巨大な1つのRailsアプリからリファクタリング。
以下の制約。
- フォルダ構成をドメイン割りに(各ドメインフォルダ下にcontroller,model,viewなどが存在)
- ドメイン間での直接の依存は禁止(ドメイン間でのコード呼び出しはインターフェイスを介する )

私見
モジュラーモノリスは結構ふんわりした概念じゃないかという印象。
「コードベースが単一かつデプロイ先も単一だが、サービスの機能単位でバックエンドコードに境界が存在。」程度のものっぽい。
個人的には、マイクロサービスみたいにシステム設計に寄った話だと思っていたが、バックエンドコードの設計の話に感じた。
マイクロサービスでは物理的にも分断するのでDBも必然的に分断されるが、モジュラーモノリスの場合はここに関しては特に制限がない。
基本的にドメイン単位で境界設定することになるので、明らかに特定ドメインに属するトランザクションテーブル(例:発注テーブル)などは特定ドメインコードからしかアクセスされないだろうが、マスタ系テーブル(例:商品マスタ)などは色々なドメインコードから参照されることになりそう。
DDDと近くね?
...そして調べる中で思ったのが、これって結構DDD設計に思想近くね?と感じた。
DDDもエヴァンス本では、パッケージ分けはドメイン単位推奨なので基本的にApp層とDomain層はドメイン分けになる。View層は昨今バックエンドは持たないので関係なしと考えて、Cotrollerは正直ただの入出力⇄ドメインオブジェクトの変換層なので、分割しようしまいがあんまり影響のない薄い層...(ここはフォルダ構成の話なので本筋ではないけど)
DBもDDD設計していると自然と特定ドメインに属するトランザクションテーブルは特定ドメインフォルダしか参照しなくなり、マスタテーブルは色んなところから参照される。
ドメイン間に明確なI/F定義するところが異なりの本筋になるかと思ったが、
DDDはメインのロジックはAPPサービスが担う筈で、このサービスクラスがほぼドメイン境界になっており、ドメイン間に結構自然とドメイン境界出来てる感があるんだよね。これをinterface挟んで疎結合にしたらそれってAPI境界だよねって気がした。。。結構似てね?
(てか最早これって手続き的コードとオブジェクト的コードの差じゃねと思う。典型的RailsのMVC構成ってザ⭐️手続型コードって感じで、DDDとかは部品化(オブジェクト思考)コードになると思っている。部品化は基本単一責任な実装にすると思うので、自然とドメイン的にも分割される事が多いのでは?と感じる。shopfiy事例はこれをRailsの上位パッケージからフォルダ分けして、フォルダ間をinterfaceを挟むことにしたものをモジュラモノリスと言っている?と感じた。)

私見2
個人的にモノリス-マイクロサービスのシステム設計ってこんな感じに分かれるんじゃないのだろうかと調べてて思った。
- モノリス
- モジュラーモノリス Lv.1(バックエンドコードのみ分離)
- モジュラーモノリス Lv.2(論理的に完全に分離)
- マイクロサービス
モジュラーモノリス Lv.1
バックエンドコードのみを論理的に分割する。(shopfiy事例のようなモジュラーモノリス。)
バックエンドソースが明確にドメイン分割しドメイン間を乗り越える場合は明確にI/F定義しインタフェース介してやり取りする。DBに関しては特段制約はなし。
そのためLv1は、バックエンドコードの設計の話になり、システム全体に対しての制約はない。(DDDっぽくやってたらこの形態に近くなってるはず。バックエンドコードはドメインを単位に論理的に分解されている筈なので)
モジュラーモノリス Lv.2
バックエンドコードに加えDBも論理的に分割し、システムとして論理的に分割されている状態。
バックエンドコードはLv.1と同様で、DBに対しても明確に分離する。
分割手法例
- ドメイン単位でスキーマ作成し論理的分割する
- DBユーザをドメイン単位で作成し付与する参照権限で論理的分割する
共通マスタテーブルに関しては、どのドメインからも参照できるようにすることが可能なため結構柔軟な気がする。(マイクロサービスの場合って共通マスタってどうしてんだろ?)
システム全体は物理的には単一だが、論理的には複数の状態。
マイクロサービス
物理的に分割されている(当然、論理的にも分割されている)
モジュラーモノリス Lv.2の論理的分割単位で物理的にも分割するイメージ。
モジュラーモノリス Lv.2とマイクロサービスの比較
この二者の違いは論理的分割か物理的分割か。
モジュラーモノリス Lv.2目線で思いついたメリデメを記載する。
メリット
- インフラが1つでインフラ構築工数が少ない
- インフラが1つでサーバーコストが少ない
- 通信のネットワークのオーバーヘッドがない
- 共通機能、共通データは同一コードベースで利用箇所特定容易。
- 論理単位の変更も1コードベースでやり易く、インフラもないのでやり易い。
- トランザクション単位が1システムに閉じる(Sagaパターンとか不要で異常系が楽)
デメリット
- デプロイ単位が1つで多チーム運用が辛い
- 使用言語が1つのみになる
- スケールアウトが機能単位でスケール不可
- サーバーダウンでサービス完全ストップ
思ったこと
少人数チームでサービスの利用者も少なく、システム自体も変更が想定されるのであればモジュラーモノリス Lv.2の方が魅力的な気がする。
マイクロサービスの利点って、論理的に分割していることからの利点(主に影響範囲閉じ込めなどの開発面)と、物理的に分割していることからの利点(主にスケールアウト、並列作業性などの運用面)があって、その論理的分割メリットを享受したければモジュラーモノリスLv2なのかなと。
多分、物理面のメリットってサービス利用者が増えたり、開発メンバーが多い時のサービス規模が大きくなった時のメリットな気がする。
嬉しいことにサービスが大きくなった後に容易にマイクロサービスに移行しやすいこともいい。
とはいえ、開発速度の速さは圧倒的にモノリスなので、
MVP検証:Rails系でがりがりモノリス
製品版:モジュラーモノリス Lv.2
成長後:マイクロサービス
こんな使い方とか?
モジュラーモノリス Lv.1はRails系のコード整理したい!ときや、アプリケーション新規作成時にDDD的設計してたら自然とドメイン境界が分離されてる感じか?