目次
概要
数年前にマイクロサービスが流行ったとき、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アプリケーションは変更前と同様に動作します。時間のあるときに少しづつ修正していくことで、モジュールをより疎結合にすることができます。疎結合にすることで、当初目標の認知負荷の軽減を達成できます。
まとめ
既存のモノリスをマイクロサービスに分割することは、大きな工数がかかります。しかし、モジュラーモノリスは、ツールも整備されているため、少ない工数で対応可能です。とりあえずファイルを移動させておいて、時間のかかる依存関係解消は少しづつ進めることもできます。少しでも皆様のお役に立てれば幸いです。
- 他社事例: 食べログ、STORES、NOTE、Timee ↩︎
- ディレクトリの名前も場所も自由に選択できますが、多くのプロダクトでこのように設定しているようです。 ↩︎
- 初回実行ではbundleを更新するように警告が出てくるため、指示に従い更新します。
bundle binstubs bundler –force ↩︎ - 静的解析ツールの名前はpackwerkで、名前が示す通りRails6以降で標準のローダーであるzeitwerkに依存しています。
classicローダーを使っている場合、事前にzeitwerkに移行する必要があります。 ↩︎