docbrown/docs/backend/service-objects.md
Michael Kohl 824e9a13cb
Add service generator (#11265)
* Add service generator

* Finalize service generator

* Add documentation

* Explicitly require rails/generators

* Update Zeitwerk initializer

* Remove explicit require

* Update docs/backend/service-objects.md

Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>

Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
2020-11-10 09:09:35 +07:00

114 lines
2 KiB
Markdown

---
title: Service Objects
---
## What are Service Objects
Service objects are Plain Old Ruby Objects (POROs) which encapsulate a whole
business process/user interaction.
Our services are located in `app/services` with the corresponding specs in
`spec/services`. Their main interface is a class level method named `call`, as
in the following example:
```ruby
class ImportUsers
def self.call(arg1)
new(arg1).call
end
def initialize(arg1)
@arg1 = arg1
end
def call
# import code goes here
end
end
```
To distinguish services from models we often give them verb names vs noun
names, e.g. `ImportUsers` instead of `UserImporter`.
## Generating Service Objects
To make our services more consistent we use a custom Rails generator. Some usage
examples:
**Generate a non-namespaced service without arguments**
`$ rails generate service DoTheThing`
```ruby
# app/services/do_the_thing.rb
class DoTheThing
def self.call
new.call
end
def call
end
end
```
```ruby
# spec/services/do_the_thing_spec.rb
require "rails_helper"
RSpec.describe DoTheThing, type: :service do
pending "add some examples to (or delete) #{__FILE__}"
end
```
**Generate a non-namespaced service with arguments:**
`$ rails generate service DoTheThing arg1 arg2`
```ruby
# app/services/do_the_thing.rb
class DoTheThing
def self.call(arg1, arg2)
new(arg1, arg2).call
end
def initialize(arg1, arg2)
@arg1 = arg1
@arg2 = arg2
end
def call
end
end
```
The generated spec is the same as above.
**Generate a namespaced service with arguments**
`$ rails generate service things/dothem arg1 arg2`
```ruby
# app/services/things/do_them.rb
class Things::DoThem
def self.call(arg1, arg2)
new(arg1, arg2).call
end
def initialize(arg1, arg2)
@arg1 = arg1
@arg2 = arg2
end
def call
end
end
```
```ruby
# spec/services/things/do_them_spec.rb
require "rails_helper"
RSpec.describe Things::DoThem, type: :service do
pending "add some examples to (or delete) #{__FILE__}"
end
```