0

The key word in the question is elegantly; I'm an experienced developer familiar with the mechanics of asynchronous programming, REST APIs, database triggers, programming idioms and design patterns. However I'm rather new to Salesforce.

I have a working solution using the Tell Don't Ask idiom but it feels clumsy and also rather intractable to improvement. Unfortunately corporate policy prevents me sharing the code in detail so I will need to provide a detailed exposition and my reference sources I've used to construct it. If you need elaboration I will expand any areas as much as possible.

  • Triggers on creation or update of records from salesforce, these are quite focused with regard to the single responsibility principle.
  • Call a class/module to prepare json from data in Trigger.New
  • Call a REST client class/module with the json.
  • REST client module
  • Makes an asynchronous authorisation call wrapped with using @Future(callout=true)
  • On successful REST authorisation this makes a self call to another method to submit the json data.
  • The data is sent by HTTP POST (for create) & HTTP PUT (for update) to an idempotent compliant service.
  • The service responds with success or failure
  • The service call is timely but not immediate, O(500-800ms).
  • When successful it returns a correlation ID.
  • The REST module calls a seperate update class/module to update the original salesforce record with the correlation ID.

Salesforce sources

Code smells

  • I need to carry around several pieces of disparate data, between the modules, the Salesforce record IDs, the third party system record Id and the returned event correlation ID.
  • I need to carry a pair of flags in the record to prevent the correlation ID update from looping back into the trigger causing another redundant call to the external system.
  • The slight delay caused by the asynchronous call means the record on screen after the trigger is already out of date.
  • Callout limits severely inhibit my test cases, setting up prerequisite test data and validating the expected data, blow the limits.

closed as too broad by gNerb, glls, Jayant Das, battery.cord, Himanshu Apr 29 at 3:14

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • It seems your current implementation works fine. So what is your area of concern here? – Jayant Das Apr 24 at 13:07
  • @JayantDas I'm finding that fixing the code-smells rather intractable. en.wikipedia.org/wiki/Code_smell – Martin of Hessle Apr 24 at 16:02
  • 6
    That will be definitely a broader topic to deal with and is very subjective. Without really taking a look at the code itself, I don't think there will be any recommendation that can really fit in here. That will definitely require a solid code review to identify potential issues and have a resolution for it. I totally understand your perspective here, but I think this question may be too broad for this forum. – Jayant Das Apr 24 at 16:06
  • 3
    I think you would find the patterns developed by Dan Appleman in Advanced Apex Programming helpful in considering the architecture of this solution. – David Reed Apr 25 at 15:16
1

Here is my updated answers:

I need to carry a pair of flags in the record to prevent the correlation ID update from looping back into the trigger causing another redundant call to the external system.

To prevent recursive trigger call from future callout execution and updating records after callout, you could check System.isBatch() and System.isFuture(), both are false.

Trigger code

trigger MyTrigger on CustomObject__c(after insert, after update.....)
{
    if(!System.isBatch() && !System.isFuture()) {
        for(CustomObject__c obj:Trigger.new){
            //prepare data
        }

        //make a call to Service class which will make callout
        MyClass.futureCalloutMethod(yourStringData);
    }

}

Service class

public class MyClass {
    @future(callout=true)
    public static void futureCalloutMethod(String str) {
        //make callout

        //update the Salesforce data like correlation ID

    }

}

I need to carry around several pieces of disparate data, between the modules, the Salesforce record IDs, the third party system record Id and the returned event correlation ID.

This question is not clear to me, it is totally depends how you have designed your data model.

The slight delay caused by the asynchronous call means the record on screen after the trigger is already out of date.

Yes, if you are updating same data which is displayed on the screen, it will be better to show a informational message to the user and navigate to the different screen, so that after user comes back again on the same screen he/she will see the updated information.

Otherwise, you need to perform synchronous call to external webservice and instantly update and show the data.

It depends how you are designing your system.

Callout limits severely inhibit my test cases, setting up prerequisite test data and validating the expected data, blow the limits.

If you are making callout from Salesforce (Outbound) then there are no limits of callouts if there are in different separate transactions. But external system makes callout to Salesforce (Inbound) then limit is imposed.

Refer Callout Limits and Limitations

  • Thank you. The '!System.isBatch() && !System.isFuture()' approach appears to solve my concern over flags. – Martin of Hessle Apr 24 at 16:05
  • I found it useful which is why I awarded it an up vote, however it only addresses one of my four areas of concern in the final section, so I wanted to give others the opportunity to address the others. I've give you the answer if I get nothing better in the next few days. – Martin of Hessle Apr 25 at 8:30
  • 1
    Refer my updated answer – Santanu Boral Apr 25 at 15:06

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