~2 minutes

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.)

Tagged with: programming

My first novel is coming soon-ish!

Check out Singular