Skip to main content
Last updated: 3 Mar 2025

whitehall: 1. Document use of services

Date: 2024-01-10

Status

Accepted

Context

A long-established pattern in Rails has been to have Ruby classes that sit separate from the usual Model, View, Controller approach, carrying out things like business logic, API calls etc and putting these in a app/services directory. We have used this approach for various things in this project, but recently we've noticed that we were a little inconsistent with our approach to services.

Some services carry out business logic (e.g CreateEditionService), while others make API calls, returning Ruby Data classes.

Decision

We have decided to continue our use of services for business logic, but if a service returns an object, we should have a class-level method on that class to return the object. For example:

Before

We have a data class called IceCream, which looks like this:

class IceCream < Data(:id, :flavour)
end

And a service method called GetIceCream:

class GetIceCream
  def get_ice_cream_for_van(van_id)
    api_response = call_some_api_here(van_id)
    
    IceCream.new(api_response["id"], api_response["flavour"])
  end
end

After

We move all the logic to the IceCream class:

class IceCream < Data(:id, :flavour)
  class << self
    def for_van(van_id)
      api_response = call_some_api_here(van_id)

      IceCream.new(api_response["id"], api_response["flavour"])
    end
  end
end

This will then allow us to call IceCream.for_van(van_id), which maps better with how database model classes operate in ActiveRecord.

We propose keeping Service classes around, but only in the following circumstances:

  • They do not return a model object
  • They carry out business logic which cannot easily be expressed in a model
  • They have a single call method, which operates on an instance of the class (eg. MyService.new(args).call)

Consequences

This reduces the risk of the app/services dir being a repository for all sorts of different types of code and forces us to think about organising our code in a more object-oriented way.