aumoにおけるモジュラーモノリス対応

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

まとめ

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


  1. 他社事例: 食べログSTORESNOTETimee ↩︎
  2. ディレクトリの名前も場所も自由に選択できますが、多くのプロダクトでこのように設定しているようです。 ↩︎
  3. 初回実行ではbundleを更新するように警告が出てくるため、指示に従い更新します。
    bundle binstubs bundler –force ↩︎
  4. 静的解析ツールの名前はpackwerkで、名前が示す通りRails6以降で標準のローダーであるzeitwerkに依存しています。
    classicローダーを使っている場合、事前にzeitwerkに移行する必要があります。 ↩︎

人気記事