blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2016.12.04 技術記事

Rails エンジンによる脱 Microservices 化のススメ

by kiyoshi.ikeda

#ruby

はじめに

こんにちは。ゲーム事業本部開発基盤部の池田です。

アドベントカレンダー4日目の今日は、複数の Rails アプリケーションを「ほどよく」マイクロサービス化する手法として、 Rails エンジンを用いた構成を紹介します。

Rails エンジン自体はそれほど目新しい技術ではありませんが、Ruby, Rails での開発経験のある方や、サーバサイドの開発者にとって、何かの参考になればよいな、と思います。

Rails エンジンとは何か

Rails エンジン は、Rails プラグインの一種ですが、単なるプラグインよりも強力で、 Rails アプリケーションとほぼ同等の機能を提供してくれます。

Rails エンジンは、Rails アプリケーションから次のコマンドで生成することができます。

$ bin/rails plugin new my_engine –full

実行すると、 my_engine/ ディレクトリ以下に、 app/config/routes.rb を含む Rails アプリケーションそっくりのディレクトリツリーが生成されます。

エンジンを生成した Rails アプリケーション側からは、まるで自身のアプリケーションコードの一部であるかのように扱うことができます。

そもそも、 Rails::Application クラス自体が Rails::Engine クラスを継承しており、その振る舞いを多く引き継いでいます。

なぜ Rails エンジンを採用したか

今回、担当した開発案件では、 API や管理画面機能を含む複数の Web アプリケーションが必要になることが、初めからわかっていました。

API と管理画面では、リクエスト・レスポンスのフォーマットも含め、フロントエンドに近いレイヤのニーズが大きく異なります。
従って、 View, Controller のレイヤは完全に切り離した方がよいだろうと考えられました。

一方で、データストアは共有するため、Model 層は共通化することができました。

そこで、Model 層を含む共通部分を Rails エンジンとして実装し、View, Controller を実装するそれぞれの Rails アプリケーションから利用する形を取ることにしました。

これにより、アプリケーションとしては分離しつつ、共通部分を再利用することで、開発を効率よく進めることができるようになりました。

アプリケーション構成

リポジトリとしては、Rails エンジンを共有する全ての Rails アプリケーションを、単一のリポジトリで管理しています。

ディレクトリ構成としては、以下のようになっています。

  • (root)
  • api/ … API
    • Gemfile
    • app/
    • bin/
    • config/
    • config.ru
    • lib/
    • shared -> ../shared/
    • spec/
  • admin/ … 管理画面
    • Gemfile
    • app/
    • bin/
    • config/
    • config.ru
    • lib/
    • shared -> ../shared/
    • spec/
  • shared/ … 共通エンジン
       - app/
       - config/
       - lib/
       - spec/

個々の Rails アプリケーションと Rails エンジンをそれぞれ別々のリポジトリにするやり方も考えられ、実際に試行錯誤もしましたが、結局は今の形になりました。
個々の Rails アプリケーション同士は独立していますが、アプリケーションとエンジンは互いに密結合であるため、特に開発効率の点から、今の形がベストのように思います。

config/lib/ 以下は、全アプリケーションで共有可能なものはエンジン側に置き、アプリケーションごとに実装の異なるものは、アプリケーション側のディレクトリに置くようにしています。

複数アプリケーション同居のつらいところ

上記のようなディレクトリ構成は、開発上便利ではありますが、Rails アプリケーションの標準的なディレクトリ構成でないため、ときどきレールに乗れずにつらい思いをすることがあります。

特につらいのがデプロイです。
Capistrano を使っていますが、基本的に release_path 等の各種パスが「リポジトリルート = アプリケーションのルートディレクトリ」を前提としているケースが多いです。

そのため、利用している Capistrano のプラグインに対して、いくつかモンキー・パッチを当てて問題を回避しています。

しかし、その辺りは一度仕組みを作ってしまえば、普段のアプリケーション開発で意識する必要はないので、エンジンを共有するメリットの方が勝っていると感じています。

終わりに

以上、Rails エンジンを利用した複数のアプリケーション構成について、紹介しました。

エンジンを共有し、単一のリポジトリで完結させるという点では「モノリシック」に近い構成とも言えそうです。
サービスでより多くのアプリケーションが必要になったときは、必ずしも全てをこれに乗せるべきということはないでしょうが、アーキテクチャとして選択肢の一つにはなると思います。

何かの参考になれば幸いです。

明日、5日目は @sairoutine さんです。お楽しみに!

参考

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。