๐งต Async Apex Unleashed: @future, Queueable, and Batchable with Real Patterns
Whats this Async you speak off
๐งต Async Apex Unleashed: @future, Queueable, and Batchable with Real Patterns
Whats this Async you speak off
๐ You know that momentโฆ Youโre building a powerful LWC or a Flow-powered approval engine. Everything looks clean โ until one save operation triggers 7 updates, 2 callouts, and maybe even a data sync to another org. Boom โ limits hit, errors logged, admins confused.

Thatโs when you realize: You need async Apex.
๐ง My Learning Strategy: Stay Curious, Stay Sharp
Before we dive in โ hereโs how I learn anything deeply and permanently.
๐ก I ask 5 simple questions:
- What is [concept]?
- When would you use it?
- Where would you use it?
- Why would you use it?
- How would you use it?
This forces me to live in the discovery phase of learning โ not just absorbing answers, but asking better questions. It sharpens critical thinking, builds context, and makes the knowledge actionable.
๐ What are @future, Queueable, and Batchable?
These are Salesforceโs async superheroes โ each with a job:

โ When would you use them?
Use async Apex when:
- Youโre making callouts from a trigger or after DML
- Need to avoid hitting SOQL/DML governor limits
- Processing massive data sets
- Want to defer, chain, or scale logic
- Breaking heavy logic into modular jobs

higher
๐ Where would you use them?
- Triggers offloading post-DML logic
- LWC or Flow โ Apex โ Queueable chain
- Scheduled jobs and background cleanup
- API integrations or post-callout workflows
- Data-intensive batch reports

๐ก Why would you use them?
โ Protect against limits โ Improve user experience โ Scale up background processing โ Clean separation of concerns โ Retryable, debuggable logic
๐ ๏ธ How would you use them?
โ Interface-first โ SOLID principles โ One instance per context โ Async logic lives outside the main transaction

๐ฏ Real-World Scenario: Booking Management System
After inserting new
Booking__crecords, we want to: โ Check vehicle availability (callout) โ Queue updates & notify managers โ Batch-process usage data
1๏ธโฃ @future(callout=true) โ Fire-and-Forget Callout
๐น Interface
public interface IBookingCallout {
void scheduleAvailabilityCheck(String bookingId);
}
๐น Implementation
public class BookingCallout implements IBookingCallout {
@future(callout=true)
public static void scheduleAvailabilityCheck(String bookingId) {
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:VehicleService/bookings/' + bookingId);
req.setMethod('GET');
HttpResponse res = new Http().send(req);
System.debug(res.getBody());
}
public void scheduleAvailabilityCheck(String bookingId) {
scheduleAvailabilityCheck(bookingId);
}
}

2๏ธโฃ Queueable โ Chainable Background Logic
๐น Interface
public interface IBookingQueueHandler {
void enqueueBookingUpdate(Set<Id> bookingIds);
}
๐น Implementation
public class BookingQueueHandler implements IBookingQueueHandler {
public void enqueueBookingUpdate(Set<Id> bookingIds) {
System.enqueueJob(new BookingQueueJob(bookingIds));
}
}
public class BookingQueueJob implements Queueable {
private final Set<Id> bookingIds;
public BookingQueueJob(Set<Id> bookingIds) {
this.bookingIds = bookingIds;
}
public void execute(QueueableContext context) {
List<Booking__c> updates = [SELECT Id, Status__c FROM Booking__c WHERE Id IN :bookingIds];
for (Booking__c b : updates) {
b.Status__c = 'Queued';
}
update updates;
}
}
3๏ธโฃ Batchable โ Mass Fleet Processing
๐น Interface
public interface IFleetBatchRunner {
void runFleetUsageBatch();
}
๐น Implementation
public class FleetUsageBatchRunner implements IFleetBatchRunner {
public void runFleetUsageBatch() {
Database.executeBatch(new FleetUsageBatch(), 200);
}
}
public class FleetUsageBatch implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext context) {
return Database.getQueryLocator([SELECT Id FROM Fleet__c]);
}
public void execute(Database.BatchableContext context, List<SObject> scope) {
List<Fleet__c> fleetRecords = (List<Fleet__c>) scope;
for (Fleet__c f : fleetRecords) {
f.Usage_Status__c = 'Processed';
}
update fleetRecords;
}
public void finish(Database.BatchableContext context) {
System.debug('Fleet usage report done.');
}
}

[embed]
๐งช Test It Like You Mean It
@isTest
public class BookingQueueTest {
@isTest static void testQueueLogic() {
List<Booking__c> bList = [SELECT Id FROM Booking__c LIMIT 2];
new BookingQueueHandler().enqueueBookingUpdate(new Map<Id, Booking__c>(bList).keySet());
Test.startTest();
System.enqueueJob(new BookingQueueJob(new Map<Id, Booking__c>(bList).keySet()));
Test.stopTest();
// Assert logic
}
}
๐ง Bonus: Async Apex in a Real-World Orchestration Pattern
Imagine this in a real trigger or Flow-powered framework:
- ๐ฅ Trigger inserts a
Booking__cโ delegates to service - ๐ Service:
1.Triggers @future callout
2.Enqueues Queueable for post-save actions
3.Nightly scheduled class kicks off Batchable for reporting
๐ฏ Each task is isolated, reusable, testable. โ๏ธ Easy to extend. ๐งช Safe to mock.
โ SOLID Design Highlights

๐ Conclusion
Async Apex isnโt optional anymore โ itโs how you build scalable, limit-safe systems in Salesforce.
When your app hits governor walls, or your integrations get complex โ donโt hack your way out. Design your way out with @future, Queueable, and Batchable.
Just remember: โ One class per task โ Interface it โ Mock it โ Test it โ Scale it
Because true architects donโt fear async. They orchestrate it. ๐ผ๐งต โ The SalesforceKlever way

๋ฉํ๋ฐ์ดํฐ
- post_id
- e2a7dff45c21
- slug
- async-apex-unleashed-future-queueable-and-batchable-with-real-patterns-e2a7dff45c21
- url
- https://medium.com/@kmniroi/async-apex-unleashed-future-queueable-and-batchable-with-real-patterns-e2a7dff45c21
- canonical_url
- https://medium.com/@kmniroi/async-apex-unleashed-future-queueable-and-batchable-with-real-patterns-e2a7dff45c21
- author_url
- https://medium.com/@kmniroi
- status
- ok
- fetched_at
- 2026-08-06 02:43:51