記事について
値オブジェクト、サービスオブジェクト、フォームオブジェクト、コールバックオブジェクト、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