...
4) Good practice: handling objects connected to third parties
TODOYou may have or anticipate to have a couple of integrations with platforms like Maestrano, QuickBooks, Xero, SalesForce etc.. and each of these platforms ask you to synchronize objects using their APIs. For each of these objects, you need to keep track of the foreign ids associated with the objects you've remotely created and/or received. This is what we call "connected objects". Now the question is: how to handle this properly?
Maestrano's approach and recommended way of doing this is to use an identity map table. An identity map is a join table allowing you to link a model on your side to a remote entity on the other side.
Models in your application will be identified by:
- ID: the id of the record in your model table (e.g.: invoices table)
- Class: the name of the class for your model (e.g.: Invoice)
Remote entities will be identified by:
- Provider: the partner holding the entity (e.g.: Maestrano, Acme Corp, Xero, QuickBooks etc.)
- Realm: the ID of the customer account (organization/company or user) who owns the remote entity.
- Entity: the name of the remote entity. You should be able to derive the path of the entity API endpoint from this name.
- RID: the ID of the remote entity.
The diagram below summarizes the concept:
The ID map makes it easy to write reusable code for connected objects. The pseudo code below shows how one could right a module, composing trait, interface or abstract class handling all the logic.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
#
# This class define the actual model mapped to the identity map table
#
@table='id_map'
class IdMapModel inherits MyFavoriteOrmBaseClass
integer id
string class_name
string provider
string realm
string entity
string rid
# Return the resource URL of the remote entity
function remote_url
manager = MyApiConfigManager.get_config(this.provider)
api_host = manager.api_host
api_path = manager.api_root_path
return host + api_path + "/" + this.entity.downcase
end
end |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
module ConnectedObjectModule
function id_maps
IdMapModel.where(id: this.id, class: this.class.to_string)
end
function id_map_for(provider,realm)
IdMapModel.where(id: this.id, class: this.class.to_string, provider: provider, realm: realm)
end
end |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
#
# Add connected object behaviour to an existing model
#
class MyInvoiceModel inherits MyFavoriteOrmBaseClass extend_module ConnectedObjectModule
end
# Retrieve a model
inv = MyInvoiceModel.find(123)
# Get the URL of a linked resource
inv.id_map_for("maestrano","a-group-id").resource_url |
Info | ||
---|---|---|
| ||
The example above shows one way of handling connected objects and does not pretend to be a silver bullet. You application may have different integration requirements. Do not hesitate to change, extend or simply adopt other patterns based on what you need to do! |