ロジックの分割
対象バージョン
- Ruby 2.2.2
- Ruby On Rails 4.2.3
ロジックを書く場所
言語を問わずMVCのフレームワークを使う時に以前からよく言われていた話として、ビジネスロジックをコントローラに書いてはいけないというのがあります。
しかし、単にコントローラを薄くすることだけを考えるとモデルがFatになってきたりするので、モデルに切り出すという事以外にもいくつかの方法を知っておく必要があります。
実装のパターン
1.モデルに実装する
コントローラにコードを書くのではなく、モデルにビジネスロジックを実装してコントローラを薄くします。
ただし、モデルにコードを集約し過ぎるとモデルがFatになってしまうので、本当にモデルに実装すべきかをよく考え、他の層に切り出すことも考えます。
モデルに実装するパターン
- モデルに紐付いたテーブルのレコード取得(ActiveRecordの操作)
- モデルに紐付いたテーブルをベースとした関連テーブルを含めたデータ取得
- モデルに紐付いたテーブルのデータ更新
単一テーブルの単純な更新など、シンプルな処理が該当すると思います。
2.フォームクラス
ビュー上の項目とモデルの項目に差異がある場合など、それを吸収させるものとしてモデルとビューの間にフォームクラスを差し込みます。
フォームクラスの実装例
フォームクラスはActiveModelをIncludeして実装することで、DB側に無いテーブルやテーブルの項目などの扱いが簡単になります。
例えば入力された内容のメールを送るだけのFAQ画面があった場合、以下のようにフォームクラスを定義することでバリデーションをかけることも可能です。
class FaqForm
include ActiveModel::Model
attr_accessor :message
validates :message, presence: true, length: { maximum: 1000 }
def send
....
end
end
ActiveModelをIncludeしているので、valid?
メソッドなども使えます。
@faq_form = FaqForm.new(faq_form_params)
if @faq_form.valid?
....
end
3.サービスクラス
複数のモデル操作やファイル操作などの処理を、アトミックなビジネスロジックのひとまとめの単位としてサービス層として切り出します。
サービスクラスの実装例
以下のように通常のクラスを定義します。
class DataRegistrationService
def initialize
....
end
def registrate
....
end
end
4.Concern
モデルやコントローラ間でしか使わないような共通処理はそれぞれのconcernに切り出します。
concernのディレクトリ
- app/models/concerns
- app/controllers/concerns
実装例
class Member
include User
end
module User
extend ActiveSupport::Concern
included do
attr_accessor :user_name
def self.default_user_name
"名無しさん"
end
def default_user_name?
uesr_name == self.class.default_user_name
end
end
end
5.ヘルパの活用
ビュー側で使うような見た目に関するものはヘルパで実装します。
例えば名前に敬称をつける場合など。
module UsersHelper
def display_name(firstname, lastname)
"#{lastname} #{first name} さん"
end
end
ただし、苗字 + 名前 でフルネームみたいなものは、モデル側でメソッドを実装して仮想化カラムのように扱うこともあります。
6.DraperなどのGemを使う
モデルとビューの中間に位置するようなプレゼンター層を扱えるGemもいくつか出ているのでそれを使うのも選択肢としてあると思います。