TL;DR
- Railsアプリにサービス層を導入したいんだけど、Trailblazerとか使うほどでもないんだよなぁ、という人(主に自分)向け
- 素のRailsアプリに自作サービス層を導入してみた
- ついでに
rails generate service hoge
みたいなジェネレータも作成してみた
経緯
既存のRailsアプリ(APIサーバ)にサービス層を導入しよう!と思い立ったものの、
Trailblazer等のgemを導入すると大掛かりな改修が必要になるため、少々敷居が高いと感じました。
そこで、既存プロジェクトにgemを追加せず、最低限の機能を持ったコンパクトなサービス層を自作して導入しようと考えました。
要件(サービス層に求めていたこと)
- 既存のRailsプロジェクトにgemを追加せずに導入できること
rails generate service sample
のように、ジェネレータを用いてファイルを作成できること- 1機能 1クラスのコンパクトなサービスクラス
- パラメータのバリデーションを行い、異常があれば例外を投げる(既存コードと同様のエラー処理を行うため)
方針
ActiveModel::Model
をincludeしたクラス(フォームオブジェクトのようなもの)を作成する- 処理実行前のバリデーション実行を強制し、パラメータが正常な場合のみ処理を行うようにする
- バリデーションエラー時には
ActiveRecord::RecordInvalid
例外を投げる rails generate generator
を使用し、ジェネレータを生成する- パラメータはコンストラクタ経由で渡す(個人的な好みです)
ソースコード
app/services/base_service.rb
すべてのサービスクラスの親クラスです。
ポイントは、
ActiveModel::Model
をincludeし、パラメータのバリデーション機能を使用可能にする- 処理実行前に必ずバリデーションを経由させる
の2点です。
1 | class BaseService |
app/services/sample_service.rb
個々のサービスクラスは、上記のBaseService
クラスを継承して作成します。
パラメータはコンストラクタ内でインスタンス変数にセットし、処理本体はperform()
メソッド内に実装します。
また、必要に応じてバリデーションのためのコードを記述します。
(コードの雛形は後述するジェネレータで自動生成します)
1 | class SampleService < BaseService |
ジェネレータ
ジェネレータは下記のコマンドで追加できます。1
$ rails generate generator service
コマンド実行後、lib/generators/service
が作成されるので、ジェネレータのコードを編集します。
ジェネレータクラスにあるすべてのインスタンスメソッドが(書いた順番に)実行されます。
1 | class ServiceGenerator < Rails::Generators::NamedBase |
また、サービスクラスとそのテストクラスのテンプレートは下記の通りです。
サービスクラスのテンプレート
1 | class <%= name.camelize %>Service < BaseService |
テストクラスのテンプレート
1 | require 'test_helper' |
使い方
サービスクラス作成
下記のコマンドを実行します
1 | $ rails generate service sample |
実行後、app/services/sample_service.rb
とtest/services/sample_service_test.rb
が作成されますので、
適宜修正してください。
controllerからの呼び出し
前述のように、パラメータはコンストラクタの引数として渡します。
また、provide()
メソッドを呼び出すことで、処理実行前に自動でバリデーションが行われます。
1 | service = SampleService.new('myuser', 'mypassword') |
まとめ
ActiveModel::Model
を利用し、コンパクトなサービス層の導入を実現しました。
また、ジェネレータも用意し、簡単にサービスクラスを追加できるようにしました。
今回作成したコードはかなり単純なものなので、もう少し改良してからgemにできたらと思います。
参考
trailblazer/trailblazer: A High-Level Architecture for Ruby.
Rails:Service層を運用して良かったところ、悪かったところ - Qiita
Rails のアーキテクチャ設計を考える - Qiita
rails でカスタム generator 作る話 - scramble cadenza
erikhuda/thor: Thor is a toolkit for building powerful command-line interfaces.