Scenario: when a record changes, query 5 external systems in parallel, combine results, write back to Salesforce.
Pattern:
`apex public class FanOutAggregator implements Queueable, Database.AllowsCallouts { private Id recordId; public FanOutAggregator(Id recordId) { this.recordId = recordId; }
public void execute(QueueableContext ctx) { // Make all callouts in parallel via Continuation (only synchronous-allowed pattern) // OR sequentially in a Queueable chain
Map<String, Object> aggregated = new Map<String, Object>();
for (String endpoint : new List<String>{'callout:CRM_System', 'callout:ERP_System', 'callout:Marketing'}) { HttpRequest req = new HttpRequest(); req.setEndpoint(endpoint + '/data/' + recordId); req.setMethod('GET'); try { HttpResponse res = new Http().send(req); if (res.getStatusCode() == 200) { aggregated.put(endpoint, JSON.deserializeUntyped(res.getBody())); } } catch (Exception e) { aggregated.put(endpoint, 'ERROR: ' + e.getMessage()); } }
// Write back to Salesforce Account a = [SELECT Id FROM Account WHERE Id = :recordId]; a.External_Data__c = JSON.serialize(aggregated); update a; } } `
The problem: Apex doesn't natively support parallel callouts. Each Http().send() is sequential. 5 callouts × 1s each = 5s — burns CPU time.
Solutions for parallelism:
- Visualforce Continuation (limited; only from VF/REST contexts) — issues up to 3 callouts in parallel and resumes when all return.
- Async fan-out — multiple Queueable jobs, each calling one endpoint, writing to a shared aggregation record:
`apex public class FanOutCoordinator implements Queueable { public void execute(QueueableContext ctx) { for (String endpoint : endpoints) { System.enqueueJob(new SingleCalloutJob(endpoint, aggregationId)); } } }
public class SingleCalloutJob implements Queueable, Database.AllowsCallouts { public void execute(QueueableContext ctx) { // make one callout, write to aggregation record } }
public class AggregationFinalizer implements Queueable { public void execute(QueueableContext ctx) { // when all SingleCalloutJobs are done, finalize } } `
Coordinate via a state record (Aggregation__c) — each child job writes its piece; a final job runs when all are complete.
- Mulesoft / external orchestrator — fan out and aggregate happens in Mulesoft; Salesforce only receives the final result. Cleanest pattern for genuine parallelism.
- Platform Events + composable subscribers — publish "needs aggregation" event; subscribers handle each endpoint; collector subscribes to all responses.
Trade-offs:
- Sequential in single Queueable: simple but slow. OK for non-time-critical work.
- Async fan-out: faster but more complex; coordinate completion.
- External orchestrator: cleanest; adds infrastructure dependency.
Reliability:
- Each child callout must handle failure independently. Don't let one failed endpoint break the whole aggregation.
- Idempotent writes — if the same callout fires twice, the result must be the same.
- Timeout each callout — avoid one slow endpoint blocking aggregation completion.
