Rails không có built-in "Service Object", nhưng pattern phổ biến: tạo class PORO (Plain Old Ruby Object) trong app/services/ để đóng gói business logic phức tạp — thay vì nhồi vào fat controller hoặc fat model.
ruby
# app/services/checkout_service.rb
class CheckoutService
def initialize(cart, user)
@cart = cart
@user = user
end
def call
ActiveRecord::Base.transaction do
order = create_order
charge_payment(order)
send_confirmation(order)
order
end
end
private
def create_order = Order.create!(user: @user, items: @cart.items)
def charge_payment(order) = PaymentGateway.charge(order)
def send_confirmation(order) = OrderMailer.confirmation(order).deliver_later
end
# Controller mỏng:
CheckoutService.new(@cart, current_user).callLợi ích: dễ test độc lập, controller đơn giản, logic không bị trùng.
Rails has no built-in "Service Object", but the common pattern is to create a PORO (Plain Old Ruby Object) in app/services/ to encapsulate complex business logic — instead of stuffing it into a fat controller or fat model.
ruby
# app/services/checkout_service.rb
class CheckoutService
def initialize(cart, user)
@cart = cart
@user = user
end
def call
ActiveRecord::Base.transaction do
order = create_order
charge_payment(order)
send_confirmation(order)
order
end
end
private
def create_order = Order.create!(user: @user, items: @cart.items)
def charge_payment(order) = PaymentGateway.charge(order)
def send_confirmation(order) = OrderMailer.confirmation(order).deliver_later
end
# Thin controller:
CheckoutService.new(@cart, current_user).callBenefits: easy to unit test in isolation, slim controllers, no duplicated logic.