この記事はAkerun - Qiita Advent Calendar 2025 -の12日目の記事です。
こんにちは、@yellow-seedです。 アドベントカレンダーに書くのは二度目になります。
フォトシンスのサービスのうち、大半を担うのはモノリスなRailsアプリケーションサーバーです。 サービスの規模が大きくなるにつれて、モノリスなアプリケーションの保守性や拡張性が低下してきました。 所属するチームは、このプラットフォームの改善のためのリアーキテクチャを推進するのがミッションのひとつです。 今回はモジュラモノリスへのリアーキテクチャに向けた準備を通じて、チームの2025年の活動を紹介したいと思います。
はじめに
2025年は本格的な着手というよりは、実行に向けた準備の期間となりました。実際に直面した課題ではなく、どのような方針で進めていくのかを決定したのか、その過程を紹介します。
2026年以降の実際の推進を通じてこの当初方針にも修正が入る可能性は十分にありえます。
モノリスサーバーを安全に分解するにあたって、『ソフトウェアアーキテクチャ・ハードパーツ』1の第I部に書かれているアプローチを参考にしています。今回は書籍でとられていたアプローチをどのように実際のプロジェクトに適用したかを紹介します。
※ ディレクトリ構造などは記事内の説明をわかりやすくするために一部省略・編集しています。
なぜやるのか
運用開始当初のアーキテクチャの想定を超えたサービス利用をつづけたことによるビジネス上の課題が顕在化してきたためです。
運用開始当初のアーキテクチャの想定を超えた例として、以下のような事例がありました。
- 当初のコア機能であった、入退室管理以外の機能を提供している
- オフィス以外のビジネスドメイン(住宅向けサービス)でも利用されている
- サービス開始当初から新規機種が追加されている
これらの事例は
- 耐障害性とスパイクリクエストに対する弾力性の低下
- バグの混入リスクの増大
- 新機能、バグ修正のデリバリー速度の鈍化
- データベース負荷の増大
として、ビジネス上のリスクをもたらしています。
どう取り組もうと考えたのか
端的には
の2段階で進めることにしました。順番に説明します。
1.コンポーネントの特定
分解の方針
歴史あるモノリスの分解戦略は
の2種類に分けられます。
予期せぬ機能への不具合の前例と現状の開発チームごとの責任範囲の曖昧さを鑑みて、いきなり戦術的フォークをして、サービスベースのアーキテクチャに移行するのはリスクが高いと判断しました。書籍の例と同様に、まずはコンポーネント分解によるアプローチを選んだということになります。
コンポーネントの候補の洗い出し
分解戦略をまず決めたので、今度は分解するコンポーネントは最終的にどのようなカテゴリに分類すべきかを決めます。
まずは、チームのメンバーで分担してAPIベースでリクエストの処理を追うことにしました。それぞれの処理内で、どのデータリソースにアクセス、操作しているかを調べて、全APIについて、以下のような調査表を作成しました。
| APIパス (Path Pattern) | ネームスペース | 調査担当 | コンポーネント名の案 |
|---|---|---|---|
/app/login (POST) |
app | 担当者 XX | login.mail |
/app/login/mfa (POST) |
app | 担当者 XX | login.mfa |
/app/login/sso (GET) |
app | 担当者 XX | login.mfa |
/app/logout (DELETE) |
app | 担当者 XX | logout |
/akc/login/recovery (POST) |
akc | 担当者 YY | login.recovery_code |
/akc/user_password/reset (PUT) |
akc | 担当者 YY | user.password |
/akc/user_password/update (POST) |
akc | 担当者 YY | user.password |
/akc/logout (DELETE) |
akc | 担当者 YY | logout |
コンポーネントの特定
コンポーネント名の案を洗い出した後、最終的にはコンポーネント名のルートレベル(上記の例でいう.のつかない属性名)が10程度にまとまるように、統廃合を行いました。上記の例で言うと login, logoutのコンポーネント名はauth、userはaccountとして集約されることになりました。
2.ディレクトリ構造を整理する
コンポーネント名の整理方針が決まったところで、コンポーネントを実際の実装に落とし込むために必要な要素を決定します。
がポイントになります。
これらの決定についてはGitHub Discussionsを利用して、Architecture Decision Record (ADR)として記録していて、いつ誰がどのようなトレードオフ条件を理解したうえで、この方針を決めたのかが追跡しやすいようにしてあります。
どの技術を使うのか?Packwerkにする
Packwerk自体はすでにさまざまなRailsアプリケーションを開発する企業テックブログで紹介されている2gemになるので、ここでは割愛します。
モジュラモノリスのアーキテクチャにおける適応度関数としての重要な役割を果たします。
Rails標準ではない学習コストのデメリットを理解しつつも、以下のメリットがコンポーネント分割の目的にかなうと判断しました。
- あくまでLintのgemでしかないので、導入自体が容易で既存動作に影響を及ぼさない。
- package_todo.yml で警告無視もできるため、段階的な導入ができる。
- CIに統合してドメイン規約違反をシステムとして検知できる。属人性を下げられる。
Packwerkのアーキテクチャのゴール像をあてはめる
分解したコンポーネントを実際のディレクトリ構成に落とし込みます。Packwerkに変更した際の実現予定のディレクトリ構造を対応させました。
将来的にクライアントごとにも分割することも想定していることから、コントローラーは従来通りのディレクトリ構造を維持しつつ、Packwerkのpacksディレクトリ以下にコンポーネントごとの責務を整理する方針としました。以下のようなイメージ構造を想定しています。
app/
assets/
controllers/
app_controller.rb ※モバイルアプリAPI
app/xxx_controller.rb
...
akc_controller.rb ※web管理システムAPI
akc/xxx_controller.rb
...
jobs/ ※基底クラスのみ
application_job.rb
mailers/ ※基底クラスのみ
application_mailer.rb
models
policies/
serializers/
validators
packs/
account/
app/
public/
account/
public_account_interaction
interactions/
jobs/
models/
lib/
tasks/
...
その他の実装ルールの決定
Serviceクラス、Formオブジェクトのインターフェースがバラバラなままその時々の開発者による増築が続けられている状況だったため、今後はActiveInteractionのgem3でそういった責務のクラスを実装していく方針、実装ルールも合わせて決めました。
現時点で予想される今後の困難さ
本来Packwerkを利用する場合は、ControllerもPackwerkのpackageディレクトリ以下に配置することが推奨されています4が、前述の通りそれを採用しないことにしました。
これは、Railsの標準的な水平アーキテクチャとドメイン中心の垂直アーキテクチャの折衷案を採用することになります。ディレクトリ構造を通じた説明の仕方をすると、以下のようになります。
# Horizontal(典型的なRails構成) app/ controllers/ # すべてのコントローラー models/ # すべてのモデル serializers/ # すべてのビュー
一方で、ドメイン中心の場合は以下のようなディレクトリ構造になります。
# Vertical(ドメイン中心でPackwerkの標準構成)
app/
domains/
users/
user_controller.rb
user.rb
devices/
device_controller.rb
device.rb
これらを踏まえると、採用しようとしている方針が両方の特徴を持っていることがわかります。
# 今回の方針は上記の折衷案(Vertial&Horizontal)
app/
assets/
controllers/
app_controller.rb ※モバイルアプリAPI
app/xxx_controller.rb
...
akc_controller.rb ※web管理システムAPI
akc/xxx_controller.rb
...
models
policies/
serializers/
validators
packs/
account/
app/
public/
account/
public_account_interaction
interactions/
jobs/
models/
lib/
tasks/
...
本来の用途をあえて活かしきらない方針で実際の実装を進めていく過程では、いろいろと悩む場面は出てくるのだろうと予想しています。
まとめ
モノリスアーキテクチャのモジュラモノリスへの移行推進以外にも、ログデータの蓄積によるデータ肥大化の解決に向けた取り組みなど、サービスの継続にともなう種々の課題が山積み状態となっています。
これらの課題の解消のための取り組みを引き続き進めていきたいと思います。
株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 career.photosynth.co.jp
Akerunにご興味のある方はこちらから akerun.com
- O'Reilly Japan, 『ソフトウェアアーキテクチャ・ハードパーツ ―分散アーキテクチャのトレードオフ分析―』↩
-
Packwerkについての参考記事:
- Packwerk で Rails アプリケーションのモジュラモノリスを実現する
- packwerk で Rails アプリケーションをパッケージ化しました
- 最適化した金融システムを Packwerk を利用して無事故でリファクタリングした話
- https://github.com/AaronLasseigne/active_interaction↩
- https://github.com/Shopify/packwerk/blob/main/USAGE.md↩