YOUTRUSTのRailsを分割した話 / Rails Engine

こんにちは、YOUTRUSTのWebエンジニア今井(YOUTRUST / X)です。

YOUTRUSTでは長らく、複数の事業ドメインを1つのRailsアプリケーション上で運用してきましたが、サービスや機能が増えるにつれ、開発・デプロイ・テストといった運用の複雑さが少しずつ目立つようになってきました。

その課題を解消するため、サービス単位でアプリケーションを分離しつつ共通基盤を保つ「Rails分割プロジェクト」を開始しました。 今回は、その中核となるRails Engine導入の背景と進め方、得られた変化について紹介します。

はじめに

YOUTRUSTでは、これまで以下のような複数のサービスを1つのRailsアプリケーションの中で運用してきました。

  • ユーザー向けのSNS機能
  • 社員が利用するAdmin
  • YOUTRUST TALENT(HR事業)
  • YOUTRUST SALES(セールステック事業)
  • YOUTRUST INSIGHT(スポットコンサルティング事業)

しかし、プロダクトが成長し、開発メンバーも増えていくにつれ、徐々に課題が見えてきました。

  • 小さな修正でもアプリ全体のデプロイが必要
  • 他サービスへの影響を把握するのが難しい
  • テスト・CI時間が増加し、デプロイサイクルが鈍化

2025年4月ごろ、新規事業の開発が始まったことをきっかけに、これらの課題を解消するため「Rails分割プロジェクト」が立ち上がりました。

YOUTRUSTでは今後も複数のサービスを横断的に展開していく構想を持っています。

今後も事業の拡大を視野に入れています

こうした複数サービスが共存・成長できる構造を実現するために、サービス単位で責務を分離しつつ共通処理は安全に再利用できる仕組みとして、「Rails Engine」を採用することにしました。

今回は、その「Rails分割プロジェクト」の過程を紹介していきます。


なぜRails Engineを選んだのか

Rails分割に取り組むにあたって、まず検討したのは「どの方法でサービス単位に分割するのが最適か?」という点でした。

当時は次のような選択肢を洗い出していました。

  • 単一Railsアプリ内で名前空間を使って論理的にサービスを分ける
  • Packwerkを導入して依存関係を明確化する
  • Rails Engineを導入して物理的にコードを分離する
  • サービス単位で完全に分離し、マイクロサービス化する

それぞれにメリットとデメリットがあります。

名前空間による分割は導入コストが低くすぐ始められますが、結局1つのアプリ内で動作するため、責務の分離には限界があります。

Packwerkは依存構造の可視化に優れていますが、コードを物理的に分けることはできません。

マイクロサービス化は理想的な独立性を実現できますが、インフラ構築や通信設計、データ同期などの運用コストが高く、現時点の規模では現実的ではありませんでした。

その中で、最もバランスが良いと判断したのが Rails Engine でした。

Rails Engineを使えば、アプリケーション間に明確な境界を設けながら、共通処理を1つの基盤にまとめることができると考えました。

YOUTRUSTでは、共通ロジックやモデルをEngineとして切り出し、その上に各サービス(SNS・採用管理・社内管理など)が独立したRailsアプリケーションとして動作する構成を採用しました。

これにより、サービスごとに責務を明確に分離しつつ、共通のデータやドメインロジックは同じ基盤上で安全に再利用できるようになりました。


Rails Engineとは

Rails Engineは、簡単に言うと「Railsアプリケーションの中で動く、小さなRailsアプリ」です。

railsguides.jp

通常のRailsアプリと同じように、app/modelsapp/controllersconfig/routes.rb を持つことができ、単体でもある程度完結した構造を作れます。

通常、Railsアプリは「1つの大きなアプリケーション」として全ての機能を抱えますが、Engineを使うと、機能単位・領域単位でコードをモジュール化できます。

アプリの中に“小さなアプリ”を入れ込むようなイメージです。

# 例:Engineを新規作成するコマンド
$ rails plugin new shared_app --mountable
  • --mountable オプションを付けることで、Engineは独自のルーティング・アセット・テスト環境を持ち、ホストアプリケーション(親アプリ)にマウントして利用できます。

ただし、YOUTRUSTでは一般的な「Engineをホストアプリにマウントする」構成ではなく、共通処理をEngineとして切り出し、各サービスが独立したRailsアプリとして動作する構成を採用しました。

構成イメージは以下のような形です。

youtrust/
├── shared_app/          # 共通ロジック・モデルを保持するRails Engine
├── backend_client_web/  # 採用担当者向けRailsアプリ
├── backend_sns/         # SNS機能を提供するRailsアプリ
└── backend_admin/       # 社内管理用Railsアプリ

各アプリケーションは shared_app を依存関係として読み込みます。

# backend_admin/Gemfile
gem 'shared_app', path: '../shared_app'

この構成により、shared_app が共通のドメインロジックやモデルを提供し、各サービス側ではそれを利用しながら独立した開発・デプロイを行えるようになっています。


Rails Engine導入の流れ

Rails Engineを導入すると決めてからは、「いきなり全てを分割する」のではなく、リスクを最小限に抑えながら段階的に移行する方針で進めました。

ここでは実際の流れを簡単に紹介します。


1. Engineの生成

共通処理を切り出すためのEngineを作成。

$ rails plugin new shared_app --mountable

この --mountable オプションを付けることで、Engineとして独立したディレクトリ構成を持つRailsアプリケーションが生成されます。

その後、既存のYOUTRUSTアプリからこのEngineを読み込むように設定します。

# backend_admin/Gemfile
gem 'shared_app', path: '../shared_app'

これで、shared_app のコードが他のアプリから利用できるようになります。


2. スキーマの移行

YOUTRUSTでは、DBスキーマの管理に Ridgepole を使用しています。

そのため、まずはスキーマ定義を shared_app 側へ移行しました。

db/Schemafile → shared_app/db/Schemafile
config/database.yml → shared_app/config/database.yml

DB自体は分離せず、1つのデータベースを全サービスで共有しています。

YOUTRUSTのデータ構造では、ユーザー情報や企業情報など複数サービスから参照されるデータが多いためです。


3. Modelの移行

次に、Modelクラスを shared_app 配下に移行していきました。

まずは依存関係が少ないモデルから順に移動し、段階的にテストを通しながら進めました。

app/models/*.rb → shared_app/app/models/*.rb

移行の過程では、関連が深いモデル(特に User まわり)を移動する際には大量のテストが失敗することもありました。

そのたびに依存関係を洗い出し、テストが通るまで地道に修正を加えていきました。


4. Command / Query の移行

モデル移行が完了した後は、更新系の処理(Command)と参照系の処理(Query)をEngineへ移行しました。

これにより、「データ構造」だけでなく「ビジネスロジック」も共通基盤側に集約することができました。


5. アプリケーション構成の整理

最終的な構成は以下を想定しています。

youtrust/
├── shared_app/          # 共通ロジック・モデルを保持するRails Engine
├── backend_client_web/  # 採用担当者向けRailsアプリ
├── backend_sns/         # SNS機能を提供するRailsアプリ
└── backend_admin/       # 社内管理用Railsアプリ

これにより、shared_app がYOUTRUST全体の“共通基盤”として機能し、各サービスはそれぞれ独立したRailsアプリとして開発・デプロイできる構成になります。

影響範囲が社内に限定されるAdminを新しいRailsアプリに切り出すところから着手しました。そのため、まだ上記で想定している構成には至っていないのが現状です。


6. インフラ構成の調整

アプリ分割に伴い、Docker ComposeやECSの設定も見直しました。

開発環境では複数Railsアプリを同時に立ち上げ、本番環境ではECSサービスをアプリ単位で分離。

今後は各サービス単位で独立デプロイを行える構成を目指しています。


Rails分割で変わったこと

影響範囲が社内に限定されるAdminのみを分割した状態なので、まだ理想とする構成とはなっていません。

今も改善を重ねながら、少しずつRails分割による変化や効果が見えてきたため、いくつか紹介します。

  • 責務の分離と分岐の削減

    サービスごとにRailsアプリを分割したことで、if service_type のような条件分岐が不要になり、コードの見通しが良くなりました。

  • CI/CDの効率化

    CI/CDの設定を見直し、変更があったサービスのみテストを実行する構成に変更しました。その結果、テスト時間を短縮できました。

  • Workerの分割による安定化

    各サービスが独立したSidekiqプロセスを持つようになり、Admin側の重いジョブ(例:メール配信)がSNS側のジョブ処理を圧迫しなくなりました。

    これによりサービス間のリソース干渉が解消されました。

    監視やアラートもサービス単位で行えるため、障害発生時もどのサービスで問題が起きているかを即座に特定できるようになりました。

  • 段階的なアップグレードが可能に

    各アプリケーションが独立した環境を持つようになったため、Adminだけ先にRailsをアップグレードするといった対応が可能になり、開発の柔軟性が高まりました。

さいごに

まだ理想の形にはたどり着けていませんが、今回の取り組みによるメリットや効果が少しずつ見えてきました。

今後も検証と改善を重ねながら、よりスケールしやすく、開発しやすい構成を目指していきます。

もし同じような取り組みをされている方がいらっしゃれば、ぜひお話を聞かせてください。

ここまで読んでくださり、ありがとうございました。


YOUTRUSTではエンジニアを募集しています。 興味のある方はぜひご応募ください。

YOUTRUSTの開発チームについて紹介させてください! | 株式会社YOUTRUSTの採用募集 | YOUTRUST

株式会社YOUTRUST の全ての求人一覧