Real cases · 10 of 25
Where AEM workflows are genuinely the right tool in real DAM and content approval scenarios, the recurring stuck-instance and duplicate-trigger problems they cause in production, and honest criteria for when a plain Sling Job beats a workflow, with a full working example.
Part of the Real-World AEM Problems series. Background reading: Workflow Architecture in AEM — Models, Steps, Payloads, and DAM Processing covers the model/step/payload concepts behind what's discussed here.
Fourth stop in the building-blocks pass: workflows. Everything so far — component, servlet, OSGi service — has been about rendering or answering a request. Workflows are different: they model an actual business process, often with a human somewhere in the loop. That distinction is exactly where the "workflow vs. just a job" decision comes from.
A content approval chain. A marketing page authored in draft needs a legal reviewer and a marketing lead to both sign off before it can be published — a workflow with participant steps routes it to each reviewer's Inbox in sequence, tracks who approved what and when, and only allows publish once both steps complete. This is a genuine business process with human decision points, not something a background job could replace.
DAM asset ingestion pipelines. An uploaded video or image needs transcoding, metadata extraction, and a move to a "ready" folder — triggered automatically by a Launcher scoped to the upload folder, with process steps handling each stage. This is the textbook AEM workflow use case, and it's built into DAM Processing Profiles for exactly this reason.
A multi-step process needing visible state and an audit trail. A legal document requiring a "reviewed → approved → archived" progression that compliance needs to be able to query later (who approved this, when) benefits from a workflow instance's built-in history — that audit trail is something you'd otherwise have to build by hand.
A stuck instance nobody notices for days. A Participant Step assigned to a reviewer who doesn't have the right group permission to see it in their Inbox leaves the instance silently suspended — the content just never gets published, and nobody gets an error, because from the system's point of view it's working exactly as configured; it's just waiting on a human who can't see the task exists. This is a genuinely common real-world failure and it's almost always a permissions issue, not a workflow engine bug.
The same asset processed twice. An overlapping Launcher path scope, or both a Launcher and a manually configured trigger pointing at the same event, produces two workflow instances for a single upload — visible as duplicate transcoding jobs or duplicate metadata updates, and confusing to debug because both instances look individually correct; the problem is that there are two of them.
A process step blocking the whole engine during high volume. A process step doing synchronous image processing (or a blocking external API call) inline, rather than handing that work off to a Sling Job, becomes a real bottleneck the moment ingestion volume spikes — this doesn't show up in normal testing with one or two test uploads, only under the kind of load a real ingestion pipeline eventually sees.
Build a workflow when: there's an actual human decision point (approval, review, sign-off), or the business genuinely needs a visible, queryable process state and audit trail — not just "something happens in the background after an event."
Skip it when: the "process" is really just a background reaction to an event with no human involvement and no need for visible state — a resource event listener or a Sling Job does the same job with far less operational overhead (no Inbox entries to manage, no workflow console to monitor, no launcher path-scoping to get right).
A genuinely common overkill pattern: building a workflow purely to sequence two or three automated background steps that nobody ever looks at in the Workflow Instances console. If no human ever needs to see or act on the state, that's a job for an event listener chaining into a Sling Job, not a workflow model — the workflow's actual value (human visibility, audit trail) is being paid for without ever being used.
A two-step approval workflow with a custom process step that hands off long-running validation to a Sling Job rather than blocking the workflow engine:
@Component(service = WorkflowProcess.class,
property = "process.label=Validate Page Before Publish Approval")
public class PageValidationProcessStep implements WorkflowProcess {
@Reference
private JobManager jobManager;
@Override
public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap metaDataMap)
throws WorkflowException {
String payloadPath = workItem.getWorkflowData().getPayload().toString();
Map<String, Object> jobProperties = new HashMap<>();
jobProperties.put("payloadPath", payloadPath);
jobProperties.put("workflowId", workItem.getWorkflow().getId());
// Validation (broken link check, required-field check) can take a
// few seconds — hand it off to a job instead of blocking this step.
jobManager.addJob("com/example/jobs/page-validation", jobProperties);
}
}
@Component(service = JobExecutor.class,
property = "job.topics=com/example/jobs/page-validation")
public class PageValidationJobExecutor implements JobExecutor {
@Reference
private WorkflowSession workflowSession;
@Override
public JobExecutionResult process(Job job, JobExecutionContext context) {
String payloadPath = (String) job.getProperty("payloadPath");
String workflowId = (String) job.getProperty("workflowId");
boolean valid = runValidation(payloadPath);
if (!valid) {
// Terminate the workflow instance and route back to the author
// rather than letting it silently continue toward publish.
terminateWithFeedback(workflowId, "Validation failed: broken links or missing required fields");
}
return context.result().succeeded();
}
private boolean runValidation(String payloadPath) {
// link checking, required-field checking against the payload
return true;
}
private void terminateWithFeedback(String workflowId, String message) {
// route workflow instance back with a comment for the author
}
}
The process step itself does almost nothing beyond kicking off the job — that's deliberate. Anything that could take more than a moment moves to a job, so the workflow engine's thread pool never becomes the bottleneck during a busy publish day.
Next in this pass: scheduler/job — where a Sling Job or a scheduled task is the leaner alternative to a workflow, the real problems with job queue backpressure and scheduler misconfiguration, and when the extra visibility a workflow provides is actually worth paying for instead.
Get an email when I publish the next chapter. No spam — just new technical deep-dives.
Comments
Share feedback or questions about this blog post.
No comments yet. Be the first to share your thoughts.