TECH BLOG

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

マイクロサービス化が進む中、Railsを用いたシステムでは「分散モノリス」の課題が顕在化しやすい。そこで、大規模Railsプロジェクトで採用され始めた「モジュラーモノリス」について紹介する。本記事では、aumoにおけるモジュラーモノリスの導入事例をもとに、その利点や実装手順、環境構築、パフォーマンス向上のポイントを詳しく解説する。モノリスの認知負荷を下げながら、疎結合な設計を実現するための方法を探る。

概要

数年前にマイクロサービスが流行ったとき、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. 他社事例: 食べログ、STORES、NOTE、Timee ↩

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

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

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

グリーグループのグリーエックス株式会社で、ソフトウェア開発に従事。広告システム開発、GREE Platformの立ち上げ、不正利用対策、チャットアプリ開発、メディア開発を経て2025年2月より現職。好きなサウナ施設は、鶯谷のサウナセンター。