Serializable Service Objects

I have to admit, I really like using Service Objects when I’m working in Rails. By which I mean, a tableless object that has domain knowledge for a particular interaction.

They make it easy to keep the bundle of logic together. . .but I’m not writing to talk about service objects in general.

This post is about how to make them ActiveJob compliant and work with Sidekiq.

Let’s say we are trying to coordinate a couple service objects, and one of them really should be run as a background job:

class Manhattan
include ActiveModel::Model
attr_accessor :ingredients

def mix_me_later
Mixer.perform_later(self)
end

def mix
# do something with the ingredients. . .
end
end

# HipsterMixer requires a great deal of time to mix
class HipsterMixer
def perform(manhattan)
manhattan.mix
end
end

Contrived example, so what’s new? ;)

Let’s introduce a concern so we can easily convert Manhattan to be serializable.

# This enables a tableless ServiceObject to be serialized by ActiveJob
module SerializableService
extend ActiveSupport::Concern

included do
include ActiveModel::Serializers::JSON
include GlobalID::Identification
end

def attributes
fail NotImplementedError, "You need to return a hash of attributes that define the service"
end

def id
attributes.to_json
end

def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end

class_methods do
def find(id)
new.from_json(id)
end
end
end

So we’re basically saying that our little class is fully definable by a GlobalID composed of whatever we put in the attributes hash.

Unsurprisingly then, we need to add that to our class.

class Manhattan
include SerializableService
attr_accessor :ingredients

def attributes
{
'ingredients' => ingredients
}
end
# . . .the same stuff as above
end

Voila, done!

Now when you pass it off to the HipsterMixer, the GlobalID generated for your background job will be a JSON-serialized representation of the Service Object.

Usual caveats are implied (be careful how much data you serialize, ids not objects, etc.)

Published under programming