Advanced 35 min read

Enforce Governor-Limit-Safe Batch Processing

By the end of this lesson, you'll be able to:

  • Write a Batch Apex class to recalculate balances for all accounts
  • Explain why a full recalculation batch job is a useful safety net
  • Size the batch scope appropriately for the workload

Prerequisites: "Build Bulk-Safe Transaction Triggers"

Why recalculate at all, if the trigger already keeps it correct

global class RecalculateBalancesBatch implements Database.Batchable<SObject> {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator([SELECT Id, Balance__c FROM Bank_Account__c]);
    }

    global void execute(Database.BatchableContext bc, List<Bank_Account__c> scope) {
        Map<Id, Decimal> sums = new Map<Id, Decimal>();
        for (AggregateResult ar : [
            SELECT Bank_Account__c acc, SUM(Amount__c) total
            FROM Transaction__c
            WHERE Bank_Account__c IN :scope AND Type__c = 'Deposit'
            GROUP BY Bank_Account__c
        ]) {
            sums.put((Id) ar.get('acc'), (Decimal) ar.get('total'));
        }
        // (a full solution also subtracts withdrawal sums per account)

        for (Bank_Account__c acc : scope) {
            acc.Balance__c = sums.containsKey(acc.Id) ? sums.get(acc.Id) : 0;
        }
        update scope;
    }

    global void finish(Database.BatchableContext bc) {}
}

Even a well-tested trigger (Module 31) can drift from correct over time — a bulk data-load bypassing the trigger, a bug fixed after some records were already affected, or a manual data correction. A periodic recalculation job, using Module 38's exact Database.Batchable shape, is a safety net that self-heals any such drift.

This is Module 38's pattern, not a new one

The start/execute/finish structure, the Database.QueryLocator for scale, and the default 200-record scope are identical to Module 38's Batch Apex lesson — a banking system doesn't need different batch mechanics, it needs the same reliable mechanics applied to a domain where "the numbers must be right" carries more weight than a typical rollup.

Choosing a smaller batch scope deliberately

Database.executeBatch(new RecalculateBalancesBatch(), 50);

Passing 50 instead of the default 200 reduces the risk of any single batch's aggregate query hitting limits when an account has an unusually large transaction history — a deliberate, documented trade-off (slower overall job, safer per-batch execution) rather than an arbitrary number.

Exercise

As a comment, explain why a bulk data load (e.g. a data migration inserting Transaction__c records directly via the Bulk API with triggers disabled) could cause Balance__c to drift out of sync, and how this batch job fixes that.

Show hint

Think about what the trigger from Lesson 2 actually depends on to run.

APEX

Enforce Governor-Limit-Safe Batch Processing Quiz

1. What is the main purpose of the RecalculateBalancesBatch job, given that the trigger already updates balances on insert?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

A Batch Apex job recalculates every account's balance from scratch against its full transaction history — a periodic correctness safety net, built with the exact Batch Apex mechanics from Module 38.