Servus Overview

Servus is a lightweight framework for implementing service objects in Ruby applications. It extracts business logic from controllers and models into testable, single-purpose classes with built-in validation, error handling, and logging.

Core Concepts

The Service Pattern

Services encapsulate one business operation. Each service inherits from Servus::Base, implements initialize and call, and returns a Response object indicating success or failure.

class ProcessPayment::Service < Servus::Base
  def initialize(user_id:, amount:)
    @user_id = user_id
    @amount = amount
  end

  def call
    user = User.find(@user_id)
    return failure("Insufficient funds") unless user.balance >= @amount

    user.update!(balance: user.balance - @amount)
    success(user: user, new_balance: user.balance)
  end
end

# Usage
result = ProcessPayment::Service.call(user_id: 1, amount: 50)
result.success? # => true
result.data     # => { user: #<User>, new_balance: 950 }

Response Objects

Services return Response objects instead of raising exceptions for business failures. This makes success and failure paths explicit and enables service composition without exception handling.

result = SomeService.call(params)
if result.success?
  result.data  # Hash or object returned by success()
else
  result.error # ServiceError instance
  result.error.message
  result.error.api_error # { code: :symbol, message: "string" }
end

Optional Schema Validation

Services can define JSON schemas for arguments and results. Validation happens automatically before/after execution but is entirely optional.

class Service < Servus::Base
  schema(
    arguments: {
      type: "object",
      required: ["user_id", "amount"],
      properties: {
        user_id: { type: "integer" },
        amount: { type: "number", minimum: 0.01 }
      }
    }
  )
end

When to Use Servus

Good fits: Multi-step workflows, operations spanning multiple models, external API calls, background jobs, complex business logic.

Poor fits: Simple CRUD, single-model operations, operations tightly coupled to one model.

Framework Integration

Servus core works in any Ruby application. Rails-specific features (async via ActiveJob, controller helpers, generators) are optional additions. Services work without any configuration - just inherit from Servus::Base and implement your logic.