Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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:

 

Image Added

 

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
languageruby
titleIdMapModel
linenumberstrue
#
# 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
languageruby
titleConnectedObjectModule
linenumberstrue
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
languageruby
titleMyInvoiceModel
linenumberstrue
#
# 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
titleDo as you see fit

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!