Everybody knows about “service objects” concept. It’s an efficient way to DRY you models and follow single responsibility principle.

Here is my vision on how to do them right: use standard CRUD interface to them (save, destroy, etc).

# app/services/base_service.rb
class BaseService
  include ActiveModel::Model
  include ActiveModel::Validations

  def self.create!(attrs)
    new(attrs).save!
  end

  def save!
    raise ActiveRecord::RecordInvalid, self unless save
  end

  def destroy!
    raise ActiveRecord::RecordInvalid, self unless destroy
  end
end

# app/services/user/trial_service.rb
class User::TrialService < BaseService
  attr_accessor :user

  validates :user, presence: true

  def save
    return false unless valid?
    ActiveRecord::Base.transaction do
      # whatever logic to start a free trial for user
    end
    true
  rescue ActiveRecord::RecordInvalid => e
    errors.add :base, e.to_s
    false
  end

  def destroy
    # end the trial for this user
  end
end

Now you have uniform interface to all of your domain logic objects (models and services) and it’s much easier to use them in controllers. Also, it forces you to think about your domain logic in terms of CRUD interface, like REST and.

In the next post I’ll show why this is very useful.