2

I have a use case like this. We need to integrate external system to make outbound calls from SFDC. Lets say, like SFDC, External System is maintaining Account and Contact.

In case of update to any contact what could be the best approach. Update operation can be performed from Visualforce or standard UI. Please note, we need to make a single synchronous call to external system.

  1. First, it will update the record in SFDC, commit the data and then make a callout?

In this case, if any error occurs from external system, we need to revert back the changes. So, possibly, we need to hold the data in some kind of map or list and revert back the changes.

  1. First, it will make a callout and if update is successful at external system then only make an update at SFDC?

In this case, if any error occurs in SFDC, then again need to call external system to revert back the changes.

In case of deleting a contact what could be the best approach. Delete operation will be perform from salesforce standard UI.

  1. First, it will delete the record in SFDC, commit the data and then make a callout?

In this case, if any error occurs from external system, we need to revert back the changes. So, possibly, (a) we need undelete the record from recycle bin and re-construct the Account-Contact relationship and revert back the changes.

  1. First, it will make a callout and if delete is successful at external system then only delete from SFDC?

Any advice, which approach to be followed. Please note that, I don't want to face this error You have uncommitted work pending. Please commit or rollback before calling out

  • since this is at the domain level, hence trigger, hence callouts not supported via trigger (must be async) – cropredy Jan 10 '17 at 0:38
  • From SFDC it would async through @future method – Santanu Boral Jan 10 '17 at 1:27
  • because the process can be initiated from visualforce, it is pretty simple to acheive all these. You can break the transaction using action functions. Callout from trigger is not a good idea. – Ashwani Jan 10 '17 at 6:39
  • which will be preferred among 4 options? – Santanu Boral Jan 10 '17 at 6:54
2

Essentially you're trying to implement a two-phase commit transaction with Salesforce yet that feature as of this writing is not supported.

Your next best option is a formal integration pattern that can capture errors (e.g. in a custom object) and report / notify people as-needed when the integration fails at any point so data inconsistencies can be fixed. So you'll really be making trade-offs on which system needs to have the change occur first and rely on that response on next actions.

Salesforce Integration Design Patterns

Please see Chapter 2: Remote Process Invocation - Request and Reply of the Salesforce Integration Patterns and Practices. This talks through similar process as you've described where a Visualforce page's controller will make Http Callout to external system and receive response.

enter image description here

Http Callouts and Uncommitted Work

Since Http Callouts cannot be done if any DML has occurred prior to the call, you will either need to:

  • make the http callout before you do Salesforce DML, or
  • do the Salesforce DML then perform http callout asynchronously such as Queueable job or @Future method

See this help article with more details on the use case and example code.

Recommendations

In case of update to any contact what could be the best approach. Update operation can be performed from Visualforce or standard UI. Please note, we need to make a single synchronous call to external system.

Option 1: I would make the http callout, if successful, then continue with DML operation in Salesforce, else display error to user. This prevents Salesforce data from being created unless external system is successful.

Option 2: Save data to Salesforce immediately. Use near real-time integration (batchable or middleware) to query for un-synced records and post to external system keeping log of any integration errors. If errors, escalate to proper teams to investigate. Salesforce user-experience will be faster, non-blocking, and external system will eventually catch up.

In case of deleting a contact what could be the best approach. Delete operation will be perform from salesforce standard UI.

Option 1: If deletion occurs from standard delete API or standard delete button then you will only know this is happening in an apex trigger. At this point you have no opportunity to make a real-time http callout due to uncommitted work. You would have to use asynchronous apex to make http callout. The record in Salesforce would be deleted and external system may or may not be successful. You can use the undelete API method in Salesforce to restore the data if necessary, but I would worry more on why external system failed to delete and keep pushing forward, not undo it.

Option 2: Override the standard Delete action with a custom Visualforce page and Apex class. Make the http callout first, if successful then perform delete DML in apex, else display error to user. This prevents Salesforce data from being deleted unless external system is successful.

Option 3: If external system is the master in your Master Data Management scenario, then deletes in Salesforce should only ever be supported as a request to delete and not allow people to physically delete in Salesforce outside of an integration user doing the DML delete operation triggered by external system.

Personal Preference if Salesforce is "Source of Truth"

I prefer to use near real-time integrations where the external system is eventually consistent with data mastered in Salesforce.

To achieve this, I would use Batchable Apex or Middleware ETL to query for inserted/updated/deleted records using below methods then make necessary Http Callouts. Ideally, the end system could receive bulk records and perform changes all at once so as to reduce the chattiness of the integration. You would need to store in perhaps a custom setting the last timestamp when you ran these methods so that you can use it as the next start time in the next calls.

  • Database.getUpdated( sobjectType, startDateTime, endDateTime ) and
  • Database.getDeleted( sobjectType, startDateTime, endDateTime )

Apex Documentation.

SOAP API Documentation.

Also be thinking about if an error occurs and a record fails to sync then it may be left out of the next window when you call getUpdated / getDeleted. Error records may need to be re-processed in a separate batch scenario, which means you'd need a flag on the record or in some tracking object to know which records are failing and still need to be resolved.

| improve this answer | |
  • thanks for your detailed answer. SFDC will be souce of truth. Then which option should I go for update and delete? – Santanu Boral Jan 12 '17 at 3:49
  • You're welcome @SantanuBoral! I've updated my answer to include a "Personal Preference" section at the end. Hope that helps! – Doug Ayers Jan 12 '17 at 4:02
  • I want that from before updating data in SFDC, it will update external system and then update in SFDC, what do you suggest? – Santanu Boral Jan 12 '17 at 4:09
1
  1. In case of update to any contact : You need to maintain a error tracking table for failure records. First commit the data in salesforce. Then system will check if any failure record is there in error tacking table for that contact before making a call out. If no records found then make call webservice asynchronously in trigger, pass the contact details to the async method. if any failure happens, capture the details in failure object and revert the changes you done.

  2. In case of deleting a contact: Prepare a Map which you are deleting and then delete the record in SFDC, commit the data and then make a callout. Pass the Map to the async method where you are making a call out. if any failure happens then restore the the data from recycle bin.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.