Railsでロジックを書く場所を意識する

  • 61
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

ロジックの分割

対象バージョン

  • 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

実装例

app/model/member.rb
class Member
  include User
end
app/model/concerns/user.rb
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もいくつか出ているのでそれを使うのも選択肢としてあると思います。