Search This Blog

2015-01-26

Create an Agnostic Vendor Infrastructure in Ruby

Depending how the code was written adding new services to it can be difficult.
For instance, let's say we are building a rails application that gets integrated to cloud storage vendors.
A naive implementation would be to directly integrate to a cloud storage specific APIs.
In this case when new cloud storage vendors are added it is very probable that several nasty case... when ... end statements would be inserted in code.

def list_folder(cloud_storage,folder)
case cloud_storage
when "dropbox"
list_dropbox_folder(folder)
when "box"
list_box_folder(folder)
when "google-drive"
list_googledrive_folder(folder)
else
raise InvalidCloudStorageService(cloud_storage)
end
end
When a new cloud storage service is added the developer must remember all places where those growing case-like statements are inserted. This makes the system more susceptible to errors due to  changes in several parts of the code. This problem is explained in detail by the shotgun surgery anti-pattern (http://en.wikipedia.org/wiki/Shotgun_surgery)

The rest of the system shouldn't be aware that a new cloudstorage service was added. That is where a combination of design patterns come in handy:
  • a facade to provide a simple interface for cloud storage operations 
  • a service locator to find the right cloud storage adapter
  • a cloud storage adapters that make each vendor API compliant to a single defined interface that can be understood by the facade


1) Cloud Storage Facade

class CloudStorageFacade
def initialize
@cloud_storage_adapter_locator = CloudStorageAdapterLocator.instance
end
def find_adapter(cloud_storage)
@cloud_storage_adapter_locator.find(cloud_storage)
end
def list_folder(cloud_storage,folder)
adapter = find_adapter(cloud_storage)
adapter.list_folder(folder)
end
# other cloud storage operations are declared here
end
2) Cloud Storage Adapter Locator

class CloudStorageAdapterLocator
def initialize
@adapters = {}
end
def self.instance
@@instance ||= CloudStorageAdapterLocator.new
@@instance
end
def find(cloud_storage)
@adapters[cloud_storage]
end
def register(cloud_storage,cloud_storage_adapter)
@adapters[cloud_storage] = cloud_storage_adapter
end
end
3) Cloud Storage Adapters

class DropboxStorageAdapter # which implements Cloud Storage Adapter protocol
def list(folder)
# specific dropbox API code goes here
# note that the output must be translated to
# a format that is known by the facade
end
# other cloud storage operations declared here
end
# register the adapter right after and keep everything in one file
DropBoxAdapterLocator.instance.register(DropboxStorageAdapter.new)
Note that this combination of patterns can be used to abstract many other types of services. Cloud storage service was just an example used here.