Real workflows: pull data from external API, transform, write to Salesforce, notify via Slack, schedule cleanup. Each step has latency, error possibilities, and dependencies.
Pattern 1: Queueable chaining — sequential async steps.
`apex public class StepOne implements Queueable { public void execute(QueueableContext ctx) { // do work System.enqueueJob(new StepTwo()); } }
public class StepTwo implements Queueable { public void execute(QueueableContext ctx) { // continue System.enqueueJob(new StepThree()); } } `
Each step is a separate transaction with fresh limits. Up to 50 chained queueables.
Pattern 2: Batch chaining — sequential batches.
apex public class BatchOne implements Database.Batchable<sObject> { public void finish(BatchableContext bc) { Database.executeBatch(new BatchTwo(), 200); } }
Useful when each step processes large data sets.
Pattern 3: Event-driven orchestration.
Step 1 publishes a Platform Event when done. A subscriber (Trigger or Flow) reacts and starts Step 2. Decouples; each step is independently retryable.
Pattern 4: State machine — orchestrator class with explicit state.
`apex public class WorkflowOrchestrator { public enum State { PENDING, EXTRACTING, TRANSFORMING, LOADING, COMPLETED } public Workflow_Run__c run;
public void advance() { switch on run.State__c { when 'Pending' -> { extract(); run.State__c = 'Extracting'; } when 'Extracting' -> { / ... / } } update run; if (run.State__c != 'Completed') System.enqueueJob(new ContinueJob(run.Id)); } } `
Persists state in a custom object. Can resume on failure. More complex but very debuggable.
Pattern 5: External orchestrator — Mulesoft / Step Functions / Airflow.
For genuinely complex workflows, the orchestrator lives outside Salesforce. Salesforce becomes one of many systems orchestrated. Trigger-based callouts publish state; the external system decides next steps.
Critical considerations:
- Idempotency — every step must be safely re-runnable. Use idempotency keys.
- Error recovery — what happens when step 3 fails? Retry? Manual review? Custom logging.
- Observability — log every step's start/end with timestamp and IDs to a
Workflow_Log__cobject. - Limits — each step has its own governor limits. Don't try to fit too much in one step.
- Testing — async chains are hard to test. Use mocks and
Test.startTest/stopTestto flush jobs.
Decision tree:
- 2-3 steps, simple data flow -> Queueable chain.
- Each step is bulk-heavy -> Batch chain.
- Loose coupling needed -> Event-driven via Platform Events.
- Complex with retry/manual review -> State machine.
- Cross-system orchestration -> External orchestrator.
A solid orchestration layer is the difference between async code that "works most of the time" and one that survives production with auditable history.
