Skip to content

Commit

Permalink
Add ADR to document our use of services
Browse files Browse the repository at this point in the history
  • Loading branch information
pezholio committed Jan 10, 2025
1 parent 88325b5 commit 82ee371
Showing 1 changed file with 75 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 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:

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

And a service method called `GetIceCream`:

```ruby
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:

```ruby
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.

0 comments on commit 82ee371

Please sign in to comment.