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