I created a service object in Rails to work as an interface between our app and our API.
I got the idea from http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Here is a small example:
class PackagesService
def self.get_package(package_id)
raise ArgumentError.new("package_id can't be nil") if package_id.blank?
package = API::get "/packages/#{package_id}"
package = JSON.parse package,
:symbolize_names => true unless package.blank?
end
end
Is there any good pattern for handling validation and/or throwing errors for Service objects?
For validations:
For errors:
Let me know if you know any good pattern or if you have better ideas to interface an API.
For simple cases (eg. with just one argument), then your check-and-raise with ArgumentError is fine. As soon as you start having complex cases (multiple arguments, objects, etc), I start leaning on Virtus and ActiveModel Validations.
Your linked article actually mentions these (see "Extract Form Objects"). I sometimes use something like these to construct service objects, eg.
require 'active_model'
require 'virtus'
class CreatePackage
include Virtus
include ActiveModel::Validations
attribute :name, String
attribute :author, String
validates_presence_of :name, :author
def create
raise ArgumentError.new("Invalid package") unless self.valid?
response = JSON.parse(
API::post("/packages", self.attributes),
:symbolize_names => true
)
Package.new(response)
end
end
class Package
include Virtus
attribute :id, Integer
attribute :name, String
attribute :author, String
end
# eg.
service = CreatePackage.new(
:name => "Tim's Tams",
:author => "Tim",
)
service.valid? # true; if false, see service.errors
package = service.create
package.attributes
# => { :id => 123, :name => "Tim's Tams", :author => "Tim" }
In so far as exceptions, I'd leave them as-is for smaller actions (like this service class). I would wrap them if I'm writing something more substantial, though, such as an entire API client library.
I would never just return nil. Things like a network error, or a bad or unparseable response from the server both benefit from explicit errors.
Finally, there's a much heavier approach called use_case. Even if you don't use it, it has a bunch of ideas as to how to tackle service objects, validations and results that you might find interesting.
Edit: Also, check out Mutations. Like use_case, except simpler and less comprehensive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With