Overloaded Args

Service Objects

Service Objects have been around in the Rails world for a while now. They don’t come as part of the framework, you create them yourself. The general principle in Rails is DRY (Don’t repeat yourself) and following this you can extract shared logic into classes which work as service objects. In Rails there are a few options for structuring logic like this, especially if you find your Models are growing quite large. You can either use concerns, which aren’t necessarily service objects or you can use classes which are not directly part of the Rails framework. People sometimes refer to Service Objects as Plain Old Ruby Objects (PORO) because they are standalone. You create a directory called services, similar to the lib directory and your classes go in there.

You can also go as far as to use some of the classes from Rails in your external service objects where you want to use that functionality. You can also have standalone projects which can require classes such as ActiveSupport to use that functionality or ActiveModel base for reusing validations, but it may be more appropriate to think about using a concern, where you use ‘include’ and then the name of your module; if you are finding your model is becoming a bit fat then you can use a concern to move that logic out. There should always be a balance however, if you are working on a large code base you may want to consider not creating too many files where it won’t be obvious where the extra logic lives. Naming conventions can help here, as also with consistency in the project where it comes to the type of pattern you are using. e.g. if the project uses lots of concerns it may be appropriate to use concerns to refactor logic and if the project uses lots of mixed in modules then it also may make sense to follow this pattern. i.e. be consistent with, libraries, concerns, modules and PORO so as not to add too much complexity. However, in Rails, there is a general understanding that usually classes that require shared logic will be better off using a concern, especially for models.

You can use the base pattern with service objects quite easily to get up and running with simple OOP and inheritance. They are really useful and can be much more flexible than using shared concerns or Rails Engines. The base pattern is basically having a class which specifies some functionality to be used in an inherited class. This is similar to abstract classes which specify an interface. Base classes are named the same as the child class but have base after the name.

I have also seen them used in conjunction with the Virtus gem for attributes or also with the command pattern, where you specify a run/execute method. They can be very useful for providing a part of an API layer to communicate with external services. Recently I found out about throttling these kind of requests with Rack attack cache throttle.

In the future Service Objects will be easily moved to serverless standalone deployable applications such through services such as AWS Lambda or being deployable through docker containers. In this way they will form part of what could be thought of as a form of Service Oriented Architecture or Microservices, but adding small standalone applications may increase the complexity of deployments so this should be a consideration.

This project is maintained by overloadedargs