Service Objects

It is best practice to separate your business logic into Service Objects rather than shoving all of it into your controllers and models. This keeps each layer focused: controllers handle HTTP, models handle persistence, and services orchestrate the actual work.

1. ActiveInteraction

One solution we really like is ActiveInteraction. It is very stable, has wonderful documentation, and gives you a clean way to build service objects with support for things like composed interactions and even ActiveModel validations.

Add the gem to your Gemfile and run bundle install:

# Gemfile
gem "active_interaction", "~> 5.3"

2. A Base Interaction

Create a base ApplicationInteraction class that all of your service objects can inherit from. This gives you a single place to add shared behavior later:

# app/interactions/application_interaction.rb
class ApplicationInteraction < ActiveInteraction::Base
  # Your interactions will inherit from this class!
end

3. Writing an Interaction

Each interaction declares its inputs with typed filters, runs validations, and implements an execute method. Here is a small example:

# app/interactions/users/sign_up.rb
module Users
  class SignUp < ApplicationInteraction
    string :email
    string :name

    validates :email, presence: true

    def execute
      User.create!(email: email, name: name)
    end
  end
end

Call it with .run (which returns an outcome you can check) or .run! (which raises on failure):

outcome = Users::SignUp.run(email: "ada@example.com", name: "Ada")

if outcome.valid?
  user = outcome.result
else
  # outcome.errors holds the validation messages
end
Pro Tip

Because interactions support composed interactions, you can call one service from another with compose, building larger workflows out of small, well-tested pieces.

Made with by Profoundry .
Copyright © 2023-2026 Profoundry LLC.
All rights reserved.