概要
数年前にマイクロサービスが流行ったとき、Rails界隈でもマイクロサービス対応が盛んに行われました。 しかし、Railsとマイクロサービスの相性はあまり良くなく、分散されたモノリスになりがちです。 そんなときRailsを使った大規模なプロダクトであるSpopifyが モジュラーモノリスという手法を提案しました。 これはモノリスの中でモジュール分割することにより、マイクロサービスの利点の一つである認知負荷を下げる試みです。 aumoでモジュラーモノリス対応を始めたため、その事例について紹介します。
サービスの成長とモノリスの限界
aumoは当初メディア事業から始まりました。 事業の成長に伴い、マーケティングSaaS、 デジタルギフトなど様々なサービスを手がけるようになりました。 これらは、Ruby on Railsのモノリスで提供しています。 しかし、それぞれのサービスは異なるドメイン知識が必要となるため、認知負荷が高い状態です。 そこで、モノリスを解体しようとしました。
まず初めに、マイクロサービスに分割できないか調査を行いました。 しかし調査をしたところ、Railsでマイクロサービス化を行なった場合、 結果としてモノリスのような密結合のマイクロサービスが生まれる 「分散モノリス」と呼ばれるアンチパターンに陥るケースが多いようです。 他社の事例1 では、モジュラーモノリス対応に切り替えるケースが多いようです。
モジュラーモノリスでは、モジュールに分割して、モジュールを疎結合にします。 これにより、認知負荷を下げます。 aumoのメディアの担当者は、デジタルギフトのドメイン知識やコードを意識しないで済むようになります。 当初の目的である認知負荷を下げることができるため、 マイクロサービスではなくモジュラーモノリス対応を行うことにしました。
gemとpacks
モジュラーモノリスは概念だけでなく、ツールが用意されています。 Rubyでモジュールというとgemを思い出しますが、 packsというモジュール機構が用意されています。 packsはrailsアプリケーションのルートディレクトリにpacksというディレクトリを作って、 そこにモジュールを格納します。2
railsからpacks以下のコードを読み込むようにパスを通すことで、 デフォルトのapp以下と同様に実装を進めることができます。 gemと異なりリポジトリの中にモジュールを作るため、 従来のCICD機構を使い続けることができます。 また、モジュール間の結合状況を静的解析するツールがあるため、開発を止めずに少しづつ移行を進めることができます。
環境構築
packsのREADMEを参考にモジュラーモノリスの環境構築を行います。 まずGemfileに以下の行を追加して、bundle installします。
# moduler monolith handling
gem 'packs-rails'
gem 'packs'
次にモジュール間の依存関係を静的解析するツールであるpackwerkを初期化3します。
bundle binstub packwerk
bin/packwerk init
モジュール作成
空のモジュールを作成します。次のコマンドでは、gift という名前のモジュールを作成します。
./bin/packs create packs/gift
作ったモジュールにファイルを移動させます。次のコマンドでは、path/to/file.rb をgiftモジュールに移動させます。
./bin/packs move packs/gift path/to/file.rb
必要なファイルを移動させたら、依存関係を静的解析4します。
root@dev:/var/source/app# ./bin/packs check
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
..........................................................E......E.....E........................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
................................................................................................................................................................
..............................................................................................................................................................
� Finished in 55.37 seconds
14 offenses detected
No stale violations detected
公式ドキュメントの解消法を参考にして、 可能な限り違反を解消します。正当な参照や修正が難しい参照は設定ファイル(package_todo.yml)に記載して、違反報告されないようにします。 以下のコマンドで、既存の違反を全て設定ファイルに書き込みます。
./bin/packs update
� Packwerk is inspecting 3838 files
..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
� Finished in 30.06 seconds
No offenses detected
✅ `package_todo.yml` has been updated.
その後再度依存関係チェックを回すと、設定ファイルに記載した違反は除外してチェックされます。 直前にupdateした場合、違反は全て設定ファイルに含まれているため、違反無しという結果となります。
./bin/packs check
..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
� Finished in 55.59 seconds
No offenses detected
No stale violations detected
モジュール外部からアクセスされるファイルはpublicディレクトリ(packs/gift/app/public/)に移動させることで、 より分かりやすくなります。
./bin/packs make_public packs/gift/path/to/file.rb
./bin/packs update
様々なコマンドを打ちましたが、結局ファイルの場所を移しただけなので、 railsアプリケーションは変更前と同様に動作します。時間のあるときに少しずつ修正していくことで、モジュールをより疎結合にすることができます。疎結合にすることで、当初目標の認知負荷の軽減を達成できます。
まとめ
既存のモノリスをマイクロサービスに分割することは、大きな工数がかかります。 しかし、モジュラーモノリスは、ツールも整備されているため、少ない工数で対応可能です。 とりあえずファイルを移動させておいて、時間のかかる依存関係解消は少しずつ進めることもできます。 少しでも皆様のお役に立てれば幸いです。