Design pattern: Service objects

  • logics do not belong to either the controller or the model
  • service object is a pattern that can help you separate business logic
  • Re-usable & easier to test

Instead of

# app/controllers/bookings_controller.rb
def create
  @booking = Booking.new(booking_params)

  if @booking.save
    UserMailer.notify_new_booking
    UserMailer.notify_admin
    ...
    redirect_to root_path
  else
    render :new
  end
end

Use

# app/services/base_service.rb
class BaseService
  def self.call(*args)
    new(*args).call
  end
end
# app/services/booking_creator.rb
class BookingCreator < BaseService
  def initialize(params)
    @params = params
  end

  def call
    booking = Booking.new(@params)

    notify_after_create_booking if booking.save

    booking
  end

  private

  def notify_after_create_booking
    UserMailer.notify_new_booking
    UserMailer.notify_admin
  end
end
# app/controllers/bookings_controller.rb
def create
  @booking = BookingCreator.call(booking_params)
  redirect_to root_path if @booking.persisted?
end