0
0

Railsにおけるロジックの切り出し方

Last updated at Posted at 2024-07-11

記事について

値オブジェクト、サービスオブジェクト、フォームオブジェクト、コールバックオブジェクト、Concernなど、Railsには多様なロジックの切り出し方法が存在します。適切に、それぞれの責務を理解して実装するべきだと思いChat GPTに聞いたりして調べたことをまとめます。

1. 値オブジェクト (Value Object)

値オブジェクトは、特定のドメイン内での値の表現を担います。これらは不変であり、値としての等価性を持ちます。たとえば、Money クラスや DateRange クラスなどが該当します。

使用例:

class Money
  attr_reader :amount, :currency

  def initialize(amount, currency)
    @amount = amount
    @currency = currency
  end

  def ==(other)
    self.amount == other.amount && self.currency == other.currency
  end
  
  def to_s
    "#{@amount} #{@currency}"
  end
  
end

# どこかのコントローラやモデル内で
price = Money.new(100, 'USD')
puts price.to_s  # "100 USD"

2. サービスオブジェクト (Service Object)

サービスオブジェクトは、1つの明確な責任を持ち、ビジネスロジックをカプセル化します。これにより、モデルが過度に複雑になるのを防ぎます。

使用例:

# app/services/user_registration_service.rb
class UserRegistrationService
  def initialize(user_params)
    @user_params = user_params
  end

  def call
    user = User.new(@user_params)
    if user.save
      # 追加のビジネスロジック
      send_welcome_email(user)
    end
    user
  end

  private

  def send_welcome_email(user)
    UserMailer.welcome_email(user).deliver_now
  end
end

##使用
# UsersController
class UsersController < ApplicationController
  def create
    @user = UserRegistrationService.new(user_params).call
    if @user.persisted?
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
    end
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :password, :password_confirmation)
  end
end

3. フォームオブジェクト (Form Object)

フォームオブジェクトは、複数のモデルにまたがるフォームのバリデーションや処理を行うために使用されます。これにより、コントローラの簡素化が図れます。

使用例:

# app/forms/user_signup_form.rb
class UserSignupForm
  include ActiveModel::Model

  attr_accessor :name, :email, :password

  validates :name, presence: true
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, presence: true, length: { minimum: 6 }

  def save
    return false unless valid?
    user = User.new(name: name, email: email, password: password)
    user.save
  end
end

##使用例

# UsersController
class UsersController < ApplicationController
  def new
    @user_signup_form = UserSignupForm.new
  end

  def create
    @user_signup_form = UserSignupForm.new(user_signup_form_params)
    if @user_signup_form.save
      redirect_to root_path, notice: 'User was successfully created.'
    else
      render :new
    end
  end

  private
  
  def user_signup_form_params
    params.require(:user_signup_form).permit(:name, :email, :password)
  end
end

4. コールバックオブジェクト (Callback Object)

コールバックオブジェクトは、特定のイベントに対して実行されるロジックをカプセル化します。これにより、モデルのコールバックの肥大化を防ぎます。

使用例:

# app/callbacks/notify_admin_on_user_signup.rb
class NotifyAdminOnUserSignup
  def self.call(user)
    AdminMailer.new_user_signed_up(user).deliver_later
  end
end

# Userモデル
class User < ApplicationRecord
  after_create_commit { NotifyAdminOnUserSignup.call(self) }
end

5. Concerns

Concernsは、モデルやコントローラに共通の機能を共有するために使用されます。特に複数のクラスに共通するロジックを抽出するのに有用です。

使用例:

# app/models/concerns/trackable.rb
module Trackable
  extend ActiveSupport::Concern

  included do
    has_many :tracking_records
  end

  def track
    # トラッキングのロジック
  end
end

# Userモデル
class User < ApplicationRecord
  include Trackable
end

# Productモデル
class Product < ApplicationRecord
  include Trackable
end

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0