AfterUpdate.Dispatcher
Collects the records that qualify and makes one bulk call per chunk with them: enqueue a Queueable, publish events or send email.
Interface
apex
public class AfterUpdate {
public interface Dispatcher extends Handler {
Boolean dispatchOnAfterUpdateWhen(TriggerHandler.UpdateRecord record);
void dispatchOnAfterUpdate(TriggerHandler.UpdateRecords records);
}
}dispatchOnAfterUpdateWhen(record): called once per record in the chunk; it only selects records; a record that has used up its recursion budget is skipped without a call. Returntrueto include the record in the dispatch.dispatchOnAfterUpdate(records): called once, after every record was checked, with the qualified records; not called when none qualified.
Example
apex
public with sharing class ContactDispatcher implements AfterUpdate.Dispatcher {
public Boolean dispatchOnAfterUpdateWhen(TriggerHandler.UpdateRecord record) {
return record.isChanged(Contact.Email);
}
public void dispatchOnAfterUpdate(TriggerHandler.UpdateRecords records) {
System.enqueueJob(new ContactSyncJob(records.getIds()));
}
public class ContactSyncJob implements Queueable {
private Set<Id> recordIds;
public ContactSyncJob(Set<Id> recordIds) {
this.recordIds = recordIds;
}
public void execute(QueueableContext context) {
}
}
}apex
public with sharing class OpportunityWonSyncDispatcher implements AfterUpdate.Dispatcher {
public Boolean dispatchOnAfterUpdateWhen(TriggerHandler.UpdateRecord record) {
return record.isChangedTo(Opportunity.StageName, 'Closed Won');
}
public void dispatchOnAfterUpdate(TriggerHandler.UpdateRecords records) {
System.enqueueJob(new SyncJob(records.getIds()));
}
public class SyncJob implements Queueable, Database.AllowsCallouts {
private Set<Id> opportunityIds;
public SyncJob(Set<Id> opportunityIds) {
this.opportunityIds = opportunityIds;
}
public void execute(QueueableContext context) {
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Erp/opportunities');
request.setMethod('POST');
request.setBody(JSON.serialize([SELECT Id, Name, Amount FROM Opportunity WHERE Id IN :this.opportunityIds]));
new Http().send(request);
}
}
}Good to Know
- One dispatch per chunk. An update of 1,000 records dispatches 5 times, so the example enqueues 5 jobs. A synchronous transaction can enqueue at most 50 Queueable jobs.
- No callouts from the trigger. A callout here throws
System.CalloutException. Call out from a Queueable that implementsDatabase.AllowsCallouts, and pass itrecords.getIds(). - No unit of work. DML here runs at once and is not merged with the Writers' changes. Use a Writer for record changes that commit with the save.
- Writers' changes may not be saved yet. By default, their registrations commit after the last handler, so a query here does not see them.
- Writer wins. A class that also implements
AfterUpdate.Writerruns only as a Writer. Its Dispatcher methods never run.
Test
apex
@IsTest
static void dispatchOnAfterUpdateWhenStageChangedToClosedWon() {
// Setup
TriggerHandler.UpdateRecord record = new TriggerHandler.TriggerRecord(new Opportunity(StageName = 'Closed Won'), new Opportunity(StageName = 'Negotiation/Review'));
// Test
Boolean result = new OpportunityWonSyncDispatcher().dispatchOnAfterUpdateWhen(record);
// Verify
Assert.isTrue(result, 'The record should qualify.');
}