afv-library/skills/generating-apex/references/automation-density-guide.md

223 lines
8.7 KiB
Markdown

# Automation Density Guide
> **Source**: Salesforce Architect Decision Guides — Record-Triggered Automation
> **Related**: [trigger-actions-framework.md](./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 `@InvocableMethod` Apex
### Coexistence Management
When an org has BOTH Flow and Apex triggers on the same object (common in brownfield orgs):
1. **Audit first**: Use **Flow Trigger Explorer** (Setup → Process Automation → Flow Trigger Explorer) to see all automations per object
2. **Document execution order**: Flows before triggers? After? Both?
3. **Consolidate incrementally**: Don't refactor everything at once
4. **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
> **`@InvocableMethod` calls 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.
```apex
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
1. Enable CDC on the object (Setup → Integrations → Change Data Capture)
2. Create CDC subscriber trigger on `{Object}ChangeEvent`
3. Process changes asynchronously with full replay capability
```apex
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
1. **Trigger/Flow**: Set a status field (e.g., `Processing_Status__c = 'Pending'`)
2. **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__c` for 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 `Schedulable` only 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
1. **Check density**: How many automations already exist on this object?
2. **Choose tool**: Low → Flow, Medium → Hybrid, High → Apex
3. **One entry point**: Don't add a second trigger or record-triggered flow if one exists
4. **Check coexistence**: Use Flow Trigger Explorer to see the full picture
5. **Consider CDC**: For external sync or non-critical async processing
6. **Consider scheduled**: For non-time-critical batch processing