adapters & APIs

22 Jul 2014

Last week, I was working on a new project that needed to use a MMS service provider to send bulk MMSes. A quick google search was all I needed to see that there were oodles of options, and to feel the accompanying apprehension of such a discovery. I knew that I could pick one now but in the future, with changing requirements or budget, or potentially even a partnership with a provider company, the service (and therefore API) we used could very easily, and very likely, change.

I needed a design solution that would be flexible and allow for simple interfacing with future unknown APIs. I had been in similar scenarios before -- for example, one product I work on has a program to read various MLS feeds and create records from very different sources and differently structured information. The solution there was to create adapters (or classes used to wrap interfaces) for the different MLS interfaces.1 This is a pretty common solution when working with vendor libraries or external APIs,2 but it's also a pattern incredibly embedded within Rails. Just consider ActiveRecord -- it has to adapt your data messages to different types of databases and return back consistent messages you can use.3

So my solution for this app was to create a service called MmsHandler and an adapter class for my chosen API. The MmsHandler would determine which API adapter to use (right now based simply on an environment variable, since we'd only be using one API at a time and this would potentially differ between prod, staging, and dev environments), give it the numbers to send out to, and tell it to perform its job. The adapter class (in this case MogreetAdapter), was responsible for invoking the external API and performing its job with the given numbers.

Here's the code (somewhat simplified):

class MmsHandler
  def self.adapter
    ENV["MMS_SERVICE_ADAPTER"].constantize.new
  end

  def self.send_mmses(numbers)
    adapter.perform(numbers)
  end
end

class MogreetAdapter
  def initialize
    @client = Mogreet::Client.new(ENV["MOGREET_CLIENT_ID"], ENV["MOGREET_SECRET"])
  end

  def perform(numbers)
    create_list && append_list(numbers) && send_mms_to_list
  end

  def create_list
    success?(@list_response = @client.list.create("new list"))
  end

  def append_list(numbers)
    success? @client.list.append(list_id: @list_response.list_id, numbers: numbers)
  end

  def send_mms_to_list
    success? @client.list.send(list_id: @list_response.list_id,
                      campaign_id: ENV["MOGREET_MMS_CAMPAIGN_ID"],
                      message: "Yay APIs!",
                      content_id: 1,
                      content_url: "http://www.ilovepies.com/pie.jpg")
  end

  def success?(response)
    response.status == "success"
  end

end

Slán go fóill,
Shelby at 16:03