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:
1 2 3 4 5 6 7 8 9 10 11 12 13class 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.
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
1 2 3 4 5 6 7 8 9# app/services/do_the_thing.rb class DoTheThing def self.call new.call end def call end end
1 2 3 4 5 6# 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14# 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14# 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
1 2 3 4 5 6# 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