Sometimes it’s really hard to find out what’s going on in a Rails app by looking into the code. Especially if it’s your first look on the source. There are lots of methods in the models and controllers and actually you need to spend some time to understand the way how the application works. When you start to wonder how to clean up the code, you feel that moving the methods from one model/controller to another one is not quite right, it neither changes too much nor improves your codebase. If you want to refactor your app, make it more maintanable, DRY and clean - you should sit down now and extract some Service Objects from your controllers.
Assume that below is the current working tree of our app. That’s a fact, we’ve omitted most of the files, but even if we write down a full list of source files below, it’s still not easy to find out what’s going on in this app by the first look. You know that we have users, some kind of events and costs there. Most likely users can log in and this kind of requests are processed by sessions controller. That’s it, there is only one thing that you can be pretty sure by looking at these files and folders: you’re working with a Rails app.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
You can make the application “first look” much more descriptive, and that’s definetely not the only advantage of using Service Objects. Compare the list above with that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
One look at the services folder and you know much more than in the previous version. You know we create events for users, which possibly can be formed in teams, the team members can be added or removed, the initial event cost should be zero etc.. Much better. Now let’s take a look on how we can refactor our controller by extracting a service object from one of it’s methods. In a normal ‘the rails way app’ we should have a ‘create’ method in our events controller, that (hmmm…?) creates an event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
As you’ve noticed there’s nothing special here. But as I think you predict: all the service objects listed above should possibly be included as methods in this controller. One method per service object gives us 5 extra methods only in this one events controller (there can be dozens of them - we call it a ‘fat controller disease’). To keep it clean and avoid the mess we create a PORO (Plain Old Ruby Object) and extract all the controller methods to service objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
The last step is to instantiate our service object in the controller and pass the event object as an argument:
1 2 3 4
and that’s it! We can now refeactor the events controller from all the other strange methods by extracting a single action to a service object line by line.
I Recommended you to read the following blog posts/articles if you want to dig in deeper into this topic:
http://blog.arkency.com/2013/09/services-what-they-are-and-why-we-need-them/ http://hawkins.io/2014/03/rethinking-application-architecture-talk/ https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ https://netguru.co/blog/service-objects-in-rails-will-help