Railsにおけるモジュラーモノリス導入事例 〜マイクロサービスとの比較と実践〜

概要

数年前にマイクロサービスが流行ったとき、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アプリケーションは変更前と同様に動作します。時間のあるときに少しずつ修正していくことで、モジュールをより疎結合にすることができます。疎結合にすることで、当初目標の認知負荷の軽減を達成できます。

まとめ

既存のモノリスをマイクロサービスに分割することは、大きな工数がかかります。 しかし、モジュラーモノリスは、ツールも整備されているため、少ない工数で対応可能です。 とりあえずファイルを移動させておいて、時間のかかる依存関係解消は少しずつ進めることもできます。 少しでも皆様のお役に立てれば幸いです。

Footnotes

  1. 他社事例: 食べログSTORESNOTETimee

  2. ディレクトリの名前も場所も自由に選択できますが、多くのプロダクトでこのように設定しているようです。

  3. 初回実行ではbundleを更新するように警告が出てくるため、指示に従い更新します。bundle binstubs bundler –force

  4. 静的解析ツールの名前はpackwerkで、名前が示す通りRails6以降で標準のローダーであるzeitwerkに依存しています。classicローダーを使っている場合、事前にzeitwerkに移行する必要があります。

Web系ベンチャーでのアドテクエンジニアを経て、2010年3月より現職。GREE Platformの立ち上げ、不正利用対策、チャットアプリ開発、メディア開発を経て2025年2月よりgreex開発に従事。