DEV Community

Rails Service Objects: A Tiny Guide

Davide Santangelo on March 04, 2023

Service objects are an important concept in the Ruby on Rails framework, and they are a crucial component of the Model-View-Controller (MVC) archit...
Collapse
 
yet_anotherdev profile image
Lucas Barret

I came from a Java SpringBoot, and it is a common pattern to use.
Now I use Rails and I really love it. Nonetheless I was quite surprised by the difference in the pattern that we use.
At the end of the day, it is really cool to improve your skills by taking what fit you in different language/framework/tech.

Collapse
 
superails profile image
Yaroslav Shmarov

absolutely agree with all the benefits you listed. service objects are always present in any ruby app I've seen.

Collapse
 
abitrolly profile image
Anatoli Babenia

It is still not convincing for me that this is better

module Library
  class CalculateShippingCost
    def initialize(cart, destination_address, shipping_method)
      @cart = cart
      @destination_address = destination_address
      @shipping_method = shipping_method
     end

    def call
        # calculate shipping cost based on @cart, @destination_address
        # and @shipping_method
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

than that

module Library
  class ShippingCost
    def calculate(cart, destination_address, shipping_method)
      # do the stuff
    end
  end
end
Enter fullscreen mode Exit fullscreen mode
Collapse
 
odineiramone profile image
Odinei Ramone

Isn't better or worse, they're just different approaches. Yours is much more semantic than the author's but the author's version have a more more consistent API. So, depends on developer to choose :D

Collapse
 
moltenhead profile image
Charlie Gardai • Edited

Something I use frequently : turn your method call static =>

module Services
  class Base
    def self.call(*args)
      new(*args).call
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Then you can extend your Services :

module Services
  class Foo < Base
  # ...
Enter fullscreen mode Exit fullscreen mode

Then simply do :

Services::Foo.call(*args)
Enter fullscreen mode Exit fullscreen mode

Also you can use a payload system or railway pattern to ensure your behaviours. And don't forget unit tests <3

Collapse
 
sergiomaia profile image
sergiomaia • Edited

Service Objects are cool, but I prefer writing modular, expressive and sequentially logical ruby code represented by use cases. With a gem like github.com/serradura/u-case we can code in a simple way (input >> process >> output) . Easy to test, easy to maintain and easy to understood. In a big app, Service Objects tends to be a transfer from fat models to fat services.

Collapse
 
hoangvn2404 profile image
Lee Nguyen

thank for posting about u-case, it look amazing to me

Collapse
 
odineiramone profile image
Odinei Ramone

really tiny and really guide! Nice! I'll send this post to my dev friends ❤️