[SalesForce] How to keep external system in Sync with Salesforce changes using REST API calls

Scenario:

  1. When a new case record is created or existing case record is updated in Salesforce, we need to send these details to an external system using REST API call.
  2. Sometimes updates happen on same record in a very short duration of time. Case status changed to 'In Progress' at 10AM and then Case status is changed to 'Closed' at 10:01AM.
  3. When updates are happening back to back, our external system is going out of sync. Because the callout from future method for the 10AM update is still in Queued status but future method callout for the 10:01AM update finished.
  4. This is resulting in external system first updating the Case status to 'Closed'(10:01AM update in Salesforce) and then re-opening(10AM update in Salesforce) the case.

Current design:

  1. We have a trigger on Case object and it calls a future method to make API call to external system.

Note:
I have looked into Platform Events and it guarantees the order of events but our external system cannot subscribe to the channel. It can only receive REST API calls.

Any better design to solve this problem?

Best Answer

Were I in your shoes, my approach would likely be to create a Custom Setting or Custom Object to stage your requests, then batch over them on as short an interval as possible. You can order your batch by CreatedDate and delete the records when finished.

public with sharing class MyBatch implements Database.Batchable<SObject>, Database.Stateful
{
    @TestVisible static Boolean chainBatches = true;

    final List<Id> successes = new List<Id>();

    public Database.QueryLocator start(Database.BatchableContext context)
    {
        return Databsae.getQueryLocator([
            SELECT ... FROM PendingSync__c
            ORDER BY CreatedDate
        ]);
    }
    public void execute(Databse.BatchableContext context, List<PendingSync__c> changes)
    {
        // make callouts
        // record successes
    }
    public void finish(Database.BatchableContext context)
    {
        Database.delete(successes);
        if (chainBatches)
        {
            system.scheduleBatch(this, 'Some Name ' + Datetime.now().format(), 5);
        }
    }
}
Related Topic