Skip to content
PLAY VIDEO PLAY VIDEO PLAY VIDEO
By Sebastian Porto

Lean models with service objects

In this post I want to describe a pattern I use a lot these days. Let me give you some background first.

While developing Rails applications it is quite common to add a lot of code into models.

For example my User class might have an instance method 'stats'. These method could do a lot of work and call many other helper methods on the way. You will often end up with models with lots of code on them.

Even worst will be the tests for that model. You will end up with a test file that covers a huge number of methods and could end up very hard to maintain.

Enter service objects

A nice way to avoid having all this logic in your model is to put your code in a different class. For example GetStatsForUserService. Then you would:

    serv = GetStatsForUserService.new(user)
    stats = serv.run

The neat thing of service objects is that your test will be very focused and easy to maintain.

Now the problem is that service objects are not very discoverable, some other developer might not realise that there is a way to get stats for a user just by looking at the user class.

Wrap your service objects in an object oriented API

To deal with discoverability a simple thing to do is to just wrap the service object inside a method in the User class itself. e.g.

    class User
        def get_stats_service
            @get_stats_service ||= GetStatsForUserService.new(self)
        end

        def stats
            get_stats_service.run
        end
    end

In this way you will be able to do user.stats and get the benefits of an OO approach and service objects all together.

Another benefit if that you can easy stub expensive methods in your tests. E.g.

    user.stub(get_stats_service).and_return(double.as_null_object)

Then you don't have to worry about your test calling a chain of expensive processes in case something happens to call user.stats.

In conclusion

  • Move expensive methods to service objects
  • Make focused test for those service objects

In your models:

  • Wrap those service objects in class/instance methods
  • In your model test just check that the wrapper method delegates to the service object, don't test the logic in the service object itself (this is already tested in the service object tests).
  • In most tests just stub the reference to the service object to return something expected.

Latest Articles by Our Team

Our expert team of designers and developers love what the do and enjoy sharing their knowledge with the world.

We Hire Only the Best

reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.

We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.

Free Community Workshops

We created the Ruby on Rails InstallFest and Ruby on Rails Development Hub to help introduce new people to software development and to help existing developers hone their skills. These workshops provide invaluable mentorship to train developers, addressing key skills shortages in the industry. Software development is a great career choice for all ages and these events help you get started and skilled up.

  • Webinars

    Webinars

    Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.

    Learn more about webinars

  • Installfest

    Installfest

    The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.

    Learn more about Installfest

  • Development Hub

    Development Hub

    The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.

    Learn more about Development Hub

Get the “reinteractive Review” Monthly Email