8.7 KiB
Automation Density Guide
Source: Salesforce Architect Decision Guides — Record-Triggered Automation Related: trigger-actions-framework.md
Automation Density Framework
Automation density = the number of automations (triggers, flows, processes) firing on a single object. Higher density increases governor limit risk and debugging complexity.
| Density | Triggers + Flows on Object | Recommended Tool | Rationale |
|---|---|---|---|
| Low (0-2) | Few automations, simple logic | Flow (Record-Triggered) | Declarative, admin-maintainable, faster to build |
| Medium (3-5) | Multiple automations, some complexity | Hybrid (Flow + Invocable Apex) | Flow orchestrates, Apex handles complex logic |
| High (6+) | Many automations, complex interdependencies | Apex (TAF or single trigger) | Full control over execution order and governor limits |
Key Decision Factors
- Team skill mix: More admins → favor Flow. More developers → favor Apex.
- Change frequency: Frequently changing business rules → Flow (no deploy cycle for admins)
- Quality assurance requirements: Apex has mature validation options. Flow validation is improving but less granular.
- Debug complexity: Multiple flows on one object are hard to debug. Apex has better stack traces.
One Entry Point Per Object
Rule: Each object should have ONE primary entry point for record-triggered automation.
Multiple triggers and record-triggered flows on the same object create:
- Unpredictable execution order between triggers and flows
- Governor limit stacking across independent automations
- Debugging nightmares when logic conflicts
Implementation Patterns
Pure Flow (Low Density):
- Single Record-Triggered Flow per object per timing (Before Save / After Save)
- Use Subflows for modularity within that single flow
Pure Apex (High Density):
- Single trigger per object → TAF MetadataTriggerHandler
- All logic in ordered Trigger Action classes
Hybrid (Medium Density):
- Record-Triggered Flow as entry point
- Complex logic delegated to
@InvocableMethodApex
Coexistence Management
When an org has BOTH Flow and Apex triggers on the same object (common in brownfield orgs):
- Audit first: Use Flow Trigger Explorer (Setup → Process Automation → Flow Trigger Explorer) to see all automations per object
- Document execution order: Flows before triggers? After? Both?
- Consolidate incrementally: Don't refactor everything at once
- Validate together: A change in one automation can break another
⚠️ Salesforce Execution Order (simplified):
Before Flows → Before Triggers → Validation Rules →
After Triggers → After Flows → Assignment Rules → Workflow Rules →
Escalation Rules → Entitlement Rules → Record-Triggered Flows (After Save)
Hybrid Pattern: Flow + Invocable Apex
The hybrid pattern uses Flow as the orchestrator and Apex for complex operations.
When to Use
- Business rules change frequently (Flow) but implementation is complex (Apex)
- Admins need to control WHEN logic runs; developers control WHAT it does
- You need Apex capabilities (complex queries, callouts, governor limit management) inside a Flow
Architecture
Record-Triggered Flow (After Save)
├── Decision: Check entry conditions
├── Action: Call @InvocableMethod (Apex)
│ └── Complex business logic, callouts, multi-object DML
└── Fault Path: Error handler
Critical Limitation
@InvocableMethodcalls from Flow execute in after-save context only. You cannot call Invocable Apex from a Before-Save Flow. Before-Save flows are limited to field updates on the triggering record.
public with sharing class ProcessOrderInvocable {
@InvocableMethod(label='Process Order' category='Orders')
public static List<Response> execute(List<Request> requests) {
List<Response> responses = new List<Response>();
for (Request req : requests) {
Response res = new Response();
try {
// Complex logic here
res.isSuccess = true;
} catch (Exception e) {
res.isSuccess = false;
res.errorMessage = e.getMessage();
}
responses.add(res);
}
return responses;
}
public class Request {
@InvocableVariable(label='Record ID' required=true)
public Id recordId;
}
public class Response {
@InvocableVariable(label='Success')
public Boolean isSuccess;
@InvocableVariable(label='Error Message')
public String errorMessage;
}
}
CDC as Async Pattern from Triggers
Change Data Capture provides a built-in async mechanism for trigger-like behavior with failure isolation.
When to Use CDC Instead of After-Save Triggers
| Factor | After-Save Trigger/Flow | CDC Subscriber |
|---|---|---|
| Timing | Same transaction | Async (separate transaction) |
| Failure impact | Rolls back triggering DML | Isolated — triggering DML succeeds |
| Replay | None | 72-hour replay window |
| Governor limits | Shared with triggering transaction | Separate transaction limits |
| Use case | Critical same-transaction logic | External sync, audit, non-critical updates |
Pattern
- Enable CDC on the object (Setup → Integrations → Change Data Capture)
- Create CDC subscriber trigger on
{Object}ChangeEvent - Process changes asynchronously with full replay capability
trigger AccountCDCSubscriber on AccountChangeEvent (after insert) {
for (AccountChangeEvent event : Trigger.new) {
String changeType = event.ChangeEventHeader.getChangeType();
if (changeType == 'UPDATE') {
List<String> changedFields = event.ChangeEventHeader.getChangedFields();
if (changedFields.contains('Status__c')) {
// Queue external sync — isolated from original transaction
System.enqueueJob(new ExternalSyncQueueable(
event.ChangeEventHeader.getRecordIds()[0]
));
}
}
}
}
Scheduled Alternative Pattern
For non-time-critical automation, use a scheduled approach instead of trigger-based automation.
Pattern: Set Status → Scheduled Job Processes
- Trigger/Flow: Set a status field (e.g.,
Processing_Status__c = 'Pending') - Scheduled Flow or Batch: Periodically queries pending records and processes them
Benefits
- Decouples trigger execution from heavy processing
- Batches multiple records for efficient governor limit usage
- Retryable — failed records stay in pending status
- Observable — query
Processing_Status__cfor pipeline visibility
Preference: Scheduled Flow over Apex Schedulable
| Factor | Scheduled Flow | Apex Schedulable |
|---|---|---|
| Deployment | Deployable metadata, packageable | Requires code deployment |
| Admin maintenance | Admins can modify schedule and logic | Developer-only |
| Job limit | No hard limit | 100 scheduled jobs max |
| Best for | Simple-to-medium scheduled tasks | Complex processing needing Batch Apex |
Recommendation: Use Scheduled Flow for most scheduled automation. Use Apex
Schedulableonly when you need Batch Apex chaining or complex Apex-only logic.
Flow Trigger Explorer
Location: Setup → Process Automation → Flow Trigger Explorer
This tool shows all automations (Flows, Process Builder, Workflow Rules) that fire on a given object, in execution order.
Use Cases
- Pre-development: Check what already exists before adding automation
- Debugging: Understand why a record update produces unexpected results
- Consolidation planning: Identify redundant or conflicting automations
- Impact analysis: Before deactivating an automation, see what else runs alongside it
Recommended Practice
Before creating any new Record-Triggered Flow or Trigger Action, check Flow Trigger Explorer to understand the existing automation landscape for that object.
Summary: Decision Checklist
- Check density: How many automations already exist on this object?
- Choose tool: Low → Flow, Medium → Hybrid, High → Apex
- One entry point: Don't add a second trigger or record-triggered flow if one exists
- Check coexistence: Use Flow Trigger Explorer to see the full picture
- Consider CDC: For external sync or non-critical async processing
- Consider scheduled: For non-time-critical batch processing