須貝氏の自己紹介

須貝俊 氏:では、「RailsでModular Monolithを選択された御社に質問したいN個の疑問」というタイトルで発表をしたいと思います。

(スライドを示して)まずは自己紹介をしたいと思います。須貝と申します。タイミーには、2022年1月からジョインしています。スポットワークシステム領域というところで、チーム名がIronBank Squadという、企業さま向けの請求や、ワーカーさまへの給与振り込みなどのお金まわりをがんばるチームに所属しています。加えて、2023年7月から、兼務でモジュラモノリス化を推進するチームにも所属しています。

本日のイベントの私の自己紹介欄にも記載しているのですが、リファクタリングが好きです。「容赦ないリファクタリング」を好きな言葉として挙げています。確かKent Beck氏の言葉だったかと思います。間違っていたらすみません。

本日のアジェンダです。まず、モジュラモノリスを取り巻く状況というところで全体の整理をしたあとに、RubyとRailsにおけるモジュラモノリス。あとタイミーとモジュラモノリス化。最後にまとめです。そういう流れで話していきたいと思います。

モノリス、モジュラモノリス、マイクロサービスについて

まず、モジュラモノリスを取り巻く状況というところで、全体の整理をしていきたいと思います。今回のイベントに参加されている方には、もしかしたらこのあたりの用語の整理とかは不要かもしれないですが、一応軽く話せればと思います。

これから話すモジュラモノリスとかマイクロサービスの説明は、だいたい『モノリスからマイクロサービスへ ―モノリスを進化させる実践移行ガイド』という書籍から拝借しています。マイクロサービスの分割の仕方自体は、モジュラモノリスにも適用できるような非常に参考になる書籍です。

まずモノリスです。モノリスは、『モノリスからマイクロサービスへ ―モノリスを進化させる実践移行ガイド』という書籍の中では「単一プロセスのモノリス」と呼ばれています。引用すると、「すべてのコードが単一のプロセスとしてデプロイされているシステム」と、記載されています。

一番シンプルな、みなさんも馴染みのあるアプリケーションのアーキテクチャなんじゃないかなと思います。これは個人的な見解ですが、モノリスのまま、シンプルなままでスケールする事業が、一番楽で良いんじゃないかと思うのですが、実際にはそんなことはないというところですかね。

次にモジュラモノリス。今回のメインになるアーキテクチャです。先ほどの、単一プロセスのモノリスの中の一種という位置づけになっています。

アプリケーションが別々のモジュールで構成されているのですが、デプロイは一緒に行う必要があるというアーキテクチャになっています。モノリスから段階的にマイクロサービスへ移行する際の、次のフェーズみたいなイメージかなと思います。これで十分というケースもよくあるとは思います。

最後にマイクロサービスですね。マイクロサービスはドメインごとにサービスが分かれていて、それぞれが独立してデプロイ可能な分散型のアーキテクチャです。

他に分散モノリスというのもありますが、あまり良くないパターンなのでここでは言及を避けます。一応どういうものかを言うと、システムは分散しているのですが、一緒にデプロイする必要があって、モノリスとマイクロサービスの両方のデメリットを持っているようなアーキテクチャになります。

なぜモジュラモノリスが最近また注目されているのか

ここからは軽く歴史的な部分にも触れます。最近、モジュラモノリスがまた注目されているような気がしていて、それはなんでなのかなと。

分散型のアーキテクチャのアイデア自体は、それこそ数十年前からあったとは思うのですが、マイクロサービスは2010年代の前半あたりから盛り上がっているような気がしています。

これは、クラウドインフラの普及やコンテナ技術の台頭とか、そういったものが背景にあるのかなと思っています。マイクロサービスアーキテクチャは、2014年にマーチン・ファウラーとジェームス・ルイスが提唱したソフトウェア開発手法の1つと言われています。コンセプトが先にあったというよりは、起きている事象に対して名前が付けられたものと言われていたりします。

国内でも数多くのプロダクトがマイクロサービスを採用したと思います。それから数年が経って、実際に運用することで良い点・悪い点が見えてきて、総括する段階に入ったのかなと個人的に推測しています。

最近公開されたt_wada(和田卓人氏)さんの『技術選定の審美眼(2023年版)』というスライドでもこの点について触れられていて。「バックエンドはマイクロサービス疲れとモジュラモノリスへの回帰」と書いてあって、僕の感覚とけっこう合っていそうだなと思いました。

Railsでモジュラモノリスを実現する3つの代表的パターン

ここまでが前段の整理の部分で、次に、「Ruby(Rails)とモジュラモノリス」というところで話したいと思います。本日のイベントのタイトルは「Rubyで追及するモジュラモノリスの可能性」なのですが、弊社はRailsを採用しているので、ここからはRailsに絞って話したいと思います。

(スライドを示して)Railsでモジュラモノリスを実現するための代表的なパターンとして、ざっくり以下の3つがあるかなと思っています。Rails Engine、モジュールのgem化、packwerkの3つです。これから各パターンを軽く説明していきますが、Rails Engineだけあまり経験がないので、ふわっとした話になることをご了承ください。

まず、Rails Engineの使用です。これはRails Engineを活用して、任意のモジュールをエンジンとして切り出す手法ですね。エンジンはどういうものかというと、説明を読むと「エンジン(engine)はホストとなるRailsアプリケーションに機能を提供するミニチュア版Railsアプリケーションとみなせます」と。単一のリポジトリに複数のRailsアプリケーションが同居するようなイメージかなと思っています。

次にgem化ですが、これはgemを活用した、任意のモジュールをgem(ライブラリ)として切り出す方法かなと思っています。これは説明することはあまりなくて、そのままかなというところです。

(スライドを示して)最後にpackwerkですが、packwerkだけちょっと手厚く紹介します。まず、これはなにかというと、Shopify製のOSSで、2020年に登場しています。

発音をドイツ語っぽく言うと正式には「パックベルク」らしいのですが、英語圏の方は「パックワーク」と呼んでいたので、私もパックワークと呼んでいます。

何をしてくれるgemなのかというと、簡単にいうと依存関係のLintなので、実行時にはまったく影響しません。弊社でも採用をしていて、ふだんは開発中にコマンドを実行したり、CIでチェックしたりということをしています。

個人的にはpackwerkのおかげでモジュラモノリス化は非常にやりやすくなっていると思っていて、Railsにおけるモジュラモノリス化への回帰の一因になっているんじゃないかなと思っています。

(スライドを示して)具体的には、こんな感じでコマンドを実行して、チェックをパスするとこのようになります。

違反があれば、具体的にどこで何の違反が起きているか、みたいなものが出力されます。

これもエンジンとけっこう似ているのですが、パッケージ内が1つのRailsアプリみたいなかたちになります。packs/xxx配下にappやspecを置いて、パッケージ内が1つのアプリになるようなイメージです。

その他の部分を補足すると、依存関係に違反があったとしても、package_todo.ymlというのに違反内容だけ記録して警告を無視できるようになっています。rubocopのrubocop_todo.ymlみたいなものをイメージしてください。

なので、段階的な移行が非常に容易になっています。あとは周辺のツールもけっこう豊富になっていて、依存関係を可視化したりするエコシステムが発展しているgemかなと思っています。

Rails Engine、gem化、packwerkの評価

ここから、それぞれを簡単に比較してみたいと思います。評価観点としては、モジュール分割の試行錯誤の容易性。可逆性と言い換えてもいいかなと思います。あとは依存関係の検査の容易性、保守性、開発者の学習コスト、再利用性というところで比較してみたいと思います。

Rails Engineは、同一リポジトリで完結させることが可能なので、比較的容易かなと思います。gem化はリポジトリを分けることになるので、分割の試行錯誤のコストはけっこう高いかなと思っています。packwerkは同一リポジトリで完結するので、比較的容易かなと。基本的にファイルの移動のみでできるので、簡単だと思います。

依存関係の検査容易性ですが、Rails Engineはエンジンの数が増えた時にけっこう大変かなという気がしています。gem化は、Bundlerを使えば依存関係をたぶん出せるのかなと推測しています。packwerkは依存関係の違反検査ツールそのものなので、これはすごく得意な領域ですね。

保守性ですが、Rails Engineは一度仕組みを作れば、たぶんそこまで労力はかからないだろうと見ています。gem化はデプロイの手間は増えそうかなと思っています。依存gemの更新作業もけっこう増えるんじゃないかなみたいなところで、保守性はちょっと高いのかなと感じています。packwerkも一度仕組みを作ればそこまで労力はかからないのかなと考えています。

続いて学習コストです。Rails EngineはRailsが用意している標準の仕組みなので情報はありますが、(学習)コストは少しあるかもしれないですね。gem化はgemを作ったことがある方であれば、(学習コストは)ほぼないかなと思います。packwerkに関しては、学習コストはやや高いかなと感じています。

再利用性は、Rails Engineは依存関係を慎重に管理すれば、再利用性は高いんじゃないかなと考えています。gemも同じく再利用性が高いと考えています。packwerkはちょっと再利用性が低いかなと感じています。再利用性に関しては、Rails Engineやgemのほうがアドバンテージがあるかなと思います。

だけど、タイミーは現状のメインの大きなプロダクトが1つあるくらいなので、そこまで再利用性を重視していませんでした。可逆性や検査のしやすさみたいなところを重視して、最終的にはpackwerkを選んでいます。

ちなみに、packwerkを使いつつRails Engineを使うことは可能だし、むしろpackwerkはエンジンの使用を推奨しています。今回挙げた3つのパターンはそれぞれ排他かというとそうでもなくて、合わせて使うことも可能なので、状況に応じて選択するのが良いかなと思います。

(次回に続く)