AEM Workflow Architecture — Designing Workflows as Business Process Orchestration
A practical developer and architect guide to AEM Workflow architecture, covering models, instances, payloads, work items, participant steps, process steps, services, repository access, failure handling, and production design.
AEM Workflow Architecture — Designing Workflows as Business Process Orchestration
AEM Workflow is easy to understand when the requirement is small.
A page is submitted.
Someone reviews it.
The page is approved.
The workflow ends.
The architecture becomes harder when the workflow starts doing more than coordinating a process.
A custom step updates metadata.
Another step sends an email.
Another calls an external API.
A participant step waits for legal approval.
A later step activates content.
Then retry logic, repository access, business validation, notifications, and integration code all begin accumulating inside workflow process classes.
At that point, the problem is no longer:
How do I create a workflow?
The useful question is:
What should the workflow own, and what should remain normal application code?
That boundary determines whether a workflow stays understandable after six months of production changes.
What a Workflow Should Own
I treat AEM Workflow primarily as a process orchestration mechanism.
It is good at representing:
- A process with multiple steps
- Human participation
- Approval and rejection
- Waiting between steps
- Routing based on process state
- A payload moving through a defined lifecycle
- Operational visibility into a running process
The workflow should describe the process.
It should not become the only place where business logic exists.
Suppose the requirement is:
Validate product content, send it for legal approval, update the approved status, and synchronize the approved product with another system.
The workflow may coordinate those stages.
The actual validation, repository update, and integration should normally live behind reusable services.
Conceptually:
Workflow
-> validation service
-> participant approval
-> product status service
-> integration handoff
The workflow owns sequencing.
The services own behavior.
That distinction makes the same business behavior usable outside the workflow when another entry point appears later.
The Main Runtime Pieces
A workflow design becomes easier to reason about when a few core objects are kept separate.
Workflow Model
The model defines the process structure.
It describes which steps exist and how the process moves between them.
The model is the reusable definition.
Workflow Instance
When a model is started for a particular payload, AEM creates a workflow instance.
That instance represents one running execution of the model.
Workflow Data
Workflow data carries information associated with the instance, including the payload.
The payload is often a repository path, but workflow data should not be treated as an arbitrary object store for the entire application.
Work Item
A work item represents work associated with a step in the running workflow.
Custom process steps commonly receive a WorkItem and use it to access
workflow data.
Workflow Session
WorkflowSession exposes operations for interacting with the workflow
engine.
It is not the same thing as a normal Sling ResourceResolver.
That distinction matters once a custom step needs repository access.
Payload Is a Business Contract
Many workflow implementations start with:
String payloadPath =
workItem
.getWorkflowData()
.getPayload()
.toString();
That code assumes the workflow model expects a path payload such as
JCR_PATH.
AEM workflows can use different payload types, so that assumption should be part of the model contract rather than an accidental cast inside every process step.
For a page or asset workflow, a repository path is often exactly the intended contract.
But it should be explicit.
If the workflow is designed for pages, then a payload such as:
/content/myproject/en/products/product-a
may be valid.
If the workflow is designed for DAM assets, the expected root and resource type may be different.
A custom process step should not blindly accept any path simply because
getPayload() returned a string.
The workflow contract should define:
- Expected payload type
- Allowed repository area
- Required resource state
- Behavior when the payload no longer exists
That validation belongs close to the boundary where workflow data becomes application input.
A Custom Process Step
A common extension point is WorkflowProcess.
For example:
@Component(
service = WorkflowProcess.class,
property = {
"process.label=My Project - Update Product Status"
}
)
public class UpdateProductStatusProcess
implements WorkflowProcess {
@Reference
private ProductStatusService
productStatusService;
@Override
public void execute(
WorkItem workItem,
WorkflowSession workflowSession,
MetaDataMap args)
throws WorkflowException {
Object payload =
workItem
.getWorkflowData()
.getPayload();
if (payload == null) {
throw new WorkflowException(
"Workflow payload is missing"
);
}
String payloadPath =
payload.toString();
productStatusService
.markApproved(
payloadPath
);
}
}
The process class is deliberately small.
It translates workflow context into a service call.
It does not contain the complete repository mutation implementation.
Why I Keep Business Logic Out of WorkflowProcess
Suppose the process step directly:
- Opens a repository session.
- Loads the page.
- Reads several properties.
- Validates product data.
- Updates approval metadata.
- Calls an external API.
- Sends an email.
- Commits repository changes.
The class now has too many reasons to change.
A change to product validation modifies the workflow step.
A change to repository structure modifies the workflow step.
A change to the external API modifies the workflow step.
Testing the business behavior also requires constructing workflow-specific objects even when the behavior itself has nothing to do with workflow.
A cleaner boundary is:
productApprovalService
.approve(payloadPath);
The workflow adapter stays small.
The service can be tested and reused independently.
Workflow Arguments Are Configuration Inputs, Not a Second Configuration System
Custom process steps can receive process arguments through
MetaDataMap.
For example:
String status =
args.get(
"PROCESS_ARGS",
String.class
);
That can be useful for small model-specific choices.
I would not put large operational configuration into a free-form process-argument string.
Values such as:
- External API endpoints
- Credentials
- Retry limits
- Environment-specific URLs
- Large path allowlists
belong in proper application configuration.
Process arguments are useful when the workflow model intentionally supplies a small parameter to the step.
OSGi configuration remains the better home for environment/runtime configuration.
Participant Steps Change the Nature of the Process
A workflow that contains only automated steps is different from a workflow that waits for a person.
Once a participant step is introduced, the workflow is now coordinating human state.
That may include:
- Who owns the work item
- Which group receives it
- How long it can remain pending
- What happens after approval
- What happens after rejection
- Whether reassignment is allowed
- How escalation/reminders are handled
This is where Workflow provides value that a chain of Sling Jobs does not.
A Sling Job can retry technical work.
It is not a human approval inbox.
Static and Dynamic Participant Selection
Some workflows can assign a participant or group directly in the model.
Other workflows need assignment based on runtime data.
For example:
- Country-specific legal group
- Brand-specific approver
- Content owner
- Business unit reviewer
That is where a dynamic participant implementation may be appropriate.
Conceptually:
@Component(
service = ParticipantStepChooser.class,
property = {
"chooser.label=My Project - Legal Reviewer"
}
)
public class LegalReviewerChooser
implements ParticipantStepChooser {
@Override
public String getParticipant(
WorkItem workItem,
WorkflowSession workflowSession,
MetaDataMap args)
throws WorkflowException {
String reviewerGroup =
resolveReviewerGroup(
workItem
);
if (reviewerGroup == null) {
throw new WorkflowException(
"Unable to resolve reviewer group"
);
}
return reviewerGroup;
}
}
The chooser should determine assignment.
It should not quietly perform unrelated business processing while AEM is asking:
Who should receive this work item?
Keep the extension point aligned with its responsibility.
Approval and Rejection Need Explicit Routes
An approval workflow usually has more than one outcome.
For example:
Legal Review
approved -> continue
rejected -> return to author
The route should represent the business process clearly.
Avoid hiding important routing decisions inside a custom process step when the workflow model itself can make the process visible.
A person looking at the model should be able to understand the major business path without reading Java code.
That is one of the main reasons to use workflow orchestration in the first place.
Repository Access Inside a Workflow Step
A WorkflowSession is not a replacement for the Resource API.
If the business service needs repository access, use the same repository-access design rules covered in Chapters 26 and 27.
The workflow process should not store a resolver as component state.
A background execution does not have an HTTP request resolver to reuse.
If the service requires a service resolver, it should use the appropriate subservice and close the resolver within the operation.
For example:
try (ResourceResolver resolver =
resolverFactory
.getServiceResourceResolver(
authInfo
)) {
Resource resource =
resolver.getResource(
payloadPath
);
if (resource == null) {
throw new ProductNotFoundException(
payloadPath
);
}
// Perform the repository operation.
}
The fact that code runs inside Workflow does not justify broad repository permissions.
Repository identity should still follow the capability being performed.
Workflow Identity and Business Authorization Are Different Questions
A workflow process may execute repository work through a service identity.
That answers:
Which repository principal performs this background operation?
It does not automatically answer:
Was the person who initiated or approved the workflow authorized to cause this business action?
Those are different security boundaries.
If a sensitive operation depends on who initiated, approved, or participated in the workflow, the application needs an explicit authorization design.
Do not treat a service user as proof that the human business action was authorized.
Do Not Carry a ResourceResolver Across Workflow Steps
A workflow can wait for minutes, hours, or days.
A resolver is not process state.
Do not place a live resolver, JCR session, or similar runtime object into workflow metadata and expect to reuse it later.
Persist identifiers and business data that can safely survive the workflow lifecycle.
Open repository access only when a step actually executes.
This is the same ownership principle from ResourceResolver design, applied to long-lived process execution.
Workflow Metadata Needs Ownership
Workflow metadata can be useful for carrying small pieces of process information.
For example:
requestReference
approvalType
sourceSystem
But metadata becomes difficult to maintain when every step writes arbitrary keys with no contract.
For important metadata, define:
- Who writes it
- Which step reads it
- Whether it is required
- Expected type
- Whether it is process state or business state
Business data that must remain valid after the workflow ends usually belongs in the business repository model, not only inside workflow metadata.
Workflow metadata should support the process.
It should not become the application's primary database.
Workflow State and Content State Are Not the Same
This distinction causes subtle production bugs.
Suppose a workflow is at:
Legal Approval
That is process state.
The page contains:
approvalStatus=pending
That is content/business state.
They may be related, but they are not automatically synchronized.
If the business requires both states, the architecture must define when content state changes and what happens if one update succeeds while another operation fails.
Do not assume that moving to a workflow step automatically means the repository content represents the same business state.
Keep External Calls Out of Long Workflow Transactions
A custom step that updates repository state and calls an external system creates a consistency problem.
For example:
- Update content to approved.
- Commit repository change.
- Call external publishing API.
- External API times out.
What is the correct workflow state now?
Retrying the entire step may repeat the repository operation and external call.
Reversing the repository change may not be safe.
For integrations that need independent retry and recovery, the workflow can hand off work to a Sling Job.
For example:
Approval complete
Workflow process creates integration job
Workflow continues or records handoff
Job performs external synchronization
Whether the workflow waits for that integration result depends on the business requirement.
The important part is not to bury a fragile network retry loop inside a process step.
Workflow and Sling Jobs Solve Different Problems
Chapter 29 established the boundary: Workflow is useful when the process itself has state; Sling Jobs are useful for discrete technical work that needs asynchronous retry/recovery.
A workflow can therefore create a job after a human approval:
Author submits content
Workflow waits for legal approval
Approval completes
Workflow creates synchronization job
Job updates external system
The approval remains workflow state. The integration retry remains job state.
What Happens When a Process Step Fails?
Failure behavior must be designed before production.
A process step can fail because:
- Payload is invalid
- Required content is missing
- Repository access fails
- Configuration is invalid
- External dependency is temporarily unavailable
- Business validation rejects the content
- A programming defect throws an unexpected exception
These failures do not all mean the same thing.
A missing required payload is not fixed by blindly retrying an external call.
A temporary downstream outage is different from invalid workflow configuration.
The process should expose enough information for an operator to distinguish permanent business/configuration failure from transient technical failure.
WorkflowException Does Not Define Technical Retry Semantics
Throwing:
throw new WorkflowException(
"Processing failed"
);
tells the workflow engine that the step did not complete normally.
It does not define a complete business retry policy.
If a technical operation requires controlled retries, delay, backoff, and independent recovery, Sling Jobs are often the better execution boundary for that part.
Use Workflow failure to represent workflow-step failure.
Do not turn exception throwing into an accidental integration retry design.
Idempotency Still Matters
A workflow process step should be safe to reason about if execution is attempted again.
Suppose a step performs:
mark content approved
send notification
create integration job
If the step partially completes and is later retried manually or through operational recovery, duplicate side effects are possible.
The design should answer:
- Is setting the approval state again safe?
- Can the notification be sent twice?
- Can the same integration job be created twice?
- How is duplicate work detected?
Workflow does not remove the idempotency questions from Chapters 29 and 30.
It adds another execution context in which those questions matter.
Avoid Giant Workflow Models
A workflow model becomes difficult to maintain when every technical detail is represented as another step.
For example:
Read metadata
Validate metadata
Set flag
Call API
Parse response
Set another flag
Send email
Create audit entry
...
A workflow model should expose meaningful process stages.
Several technical operations that together represent one business action can live behind a service invoked from one process step.
The model should be readable by someone trying to understand the process.
It should not be a visual representation of every Java method call.
Avoid One Giant Process Step Too
The opposite extreme is also a problem.
A workflow with one custom process step called:
Process Everything
that performs the entire approval lifecycle in Java defeats the purpose of using Workflow.
Human-visible states and meaningful process transitions disappear into code.
The useful boundary sits between these extremes:
- Workflow model shows important business stages.
- Custom steps adapt workflow context.
- Services implement reusable business operations.
- Jobs handle retryable technical background work.
Workflow Model Changes Need Deployment Awareness
AEM keeps a design-time workflow model under
/conf/global/settings/workflow/models. When a model is synchronized
for execution, AEM also maintains the runtime model under
/var/workflow/models.
That distinction matters in deployment design.
In AEM as a Cloud Service, Adobe recommends keeping workflow models in
the codebase so environments remain consistent. A model created only
through the UI can be affected by later code deployment if the /conf
model tree is managed by the deployment package.
For a production project, I therefore treat workflow models as deployable application artifacts rather than environment-specific manual configuration.
For significant model changes, I also test with workflows already in progress instead of assuming a newly deployed model changes every existing instance exactly as intended.
Model Design Should Survive Deployment
A workflow model should not depend on an author manually repairing production after each deployment.
Custom process labels, participant chooser registrations, configuration, service-user mappings, and repository permissions all need to be part of the deployable application design where appropriate.
If the workflow references a custom process implementation that is missing or inactive, the visual model may still exist while execution fails at runtime.
Deployment validation should therefore cover both the model and the OSGi components it depends on.
Payloads Can Disappear While a Workflow Waits
Human workflows can remain active for a long time.
During that period:
- A page can be moved.
- An asset can be deleted.
- Content can be replaced.
- Permissions can change.
- The original business state can become stale.
A process step that runs three days later should not assume the payload still looks exactly as it did when the workflow started.
Each step that depends on current repository state should validate that state when it executes.
If the business process requires an immutable snapshot, that needs a separate design.
A repository path alone does not create one.
Page Moves Are Especially Important
If workflow identity is tied only to a repository path, moving the payload during a long-running process can create problems.
The workflow may continue to reference the original path even though the content now exists elsewhere.
The correct solution depends on the business requirement:
- Prevent moves while the approval is active.
- Track a stable business identifier.
- Resolve the current content location from durable business data.
- Handle the missing original payload explicitly.
Do not silently convert a missing path into "workflow completed successfully."
Multiple Workflows on the Same Payload
Another production problem appears when the same page or asset can enter the same approval workflow more than once.
Two active instances can now compete over the same business state.
For example:
Workflow A -> legal review
Workflow B -> legal review
One is approved.
The other is rejected later.
Which state should the content contain?
The design should decide whether concurrent workflow instances are allowed for the same business object.
If not, enforce that rule at the workflow-start boundary rather than hoping authors will avoid duplicate submissions.
Starting a Workflow Is an Application Boundary
Workflow can be started from:
- AEM authoring UI
- Application code
- Servlet/service logic
- Another process
- Automation
If custom code starts a workflow, model lookup and payload validation should be explicit.
Conceptually:
WorkflowModel model =
workflowSession.getModel(
workflowModelPath
);
WorkflowData data =
workflowSession.newWorkflowData(
"JCR_PATH",
payloadPath
);
workflowSession.startWorkflow(
model,
data
);
The calling application should know which model contract it is invoking.
Avoid utilities that accept any model path and any payload path from an untrusted request.
That creates a generic workflow execution endpoint rather than a business API.
Terminate, Suspend, and Resume Are Operational Actions
Long-running workflows eventually need operational handling.
A workflow may need to be:
- Suspended
- Resumed
- Terminated
These actions affect process state and should not be treated as ordinary content edits.
If application code exposes operational controls, define:
- Who can perform the action
- Which workflow models are allowed
- What business state remains after termination
- Whether external work has already been triggered
- Whether cleanup is required
Terminating a workflow does not automatically reverse every side effect produced by earlier steps.
Workflow History Is Useful, but It Is Not Your Entire Audit Model
Workflow history helps explain how a workflow instance progressed.
That can be valuable during support investigations.
But if the business requires long-term compliance evidence, retention, immutable audit data, or reporting independent of workflow-instance lifecycle, confirm whether workflow history alone satisfies those requirements.
Do not assume an operational workflow history automatically meets every business audit requirement.
Notifications Should Follow Process Meaning
Email notifications are often added directly to multiple workflow steps.
Over time, this creates duplicated templates and inconsistent recipient logic.
If notification behavior is part of the business process, keep the workflow responsible for deciding when a notification is needed, while a notification service owns:
- Recipient resolution
- Template selection
- Rendering
- Delivery
- Provider-specific behavior
This also makes it easier to move notification delivery behind a Sling Job if retry becomes necessary.
Workflow Reminders and Escalation
Chapter 29's rw-11 scenario covered stalled approval reminders.
That scenario illustrates an important boundary.
The workflow owns the pending approval.
A separate scheduled process can detect approvals that have remained pending beyond a threshold and send reminders.
I prefer that separation when reminder timing is operational behavior rather than a state transition that must be represented directly inside every workflow instance.
If escalation itself changes the process---for example, after two days the work item must be reassigned to another participant---then the workflow model may need to represent that business rule explicitly.
Reminder and escalation are related, but they are not always the same requirement.
Workflow Launcher Is Another Trigger Boundary
AEM can start workflows in response to repository changes through Workflow Launcher configuration.
That is useful, but it should be approached with the same care as the event listeners from Chapter 30.
A broad launcher can start a large number of workflow instances during:
- Bulk imports
- Asset processing
- Metadata updates
- Package operations
- Automated repository changes
Before using a launcher, I want to know:
- Which path is observed?
- Which node/resource type is relevant?
- Which event types start the workflow?
- Can the workflow itself create changes that trigger another workflow?
- What happens during bulk operations?
- Can the same business object get multiple active instances?
A launcher is convenient.
It is still an event-to-process boundary that needs filtering and loop prevention.
Avoid Workflow Launcher Loops
A simple loop can look like this:
- Metadata change starts workflow.
- Workflow updates metadata.
- Launcher sees the update.
- Another workflow starts.
- The second workflow updates metadata again.
The fix is not to add another if somewhere deep inside the process.
The launcher and business state should be designed so that workflow-owned updates do not continuously satisfy the start condition.
Use the narrowest trigger possible and make the workflow-start condition explicit.
Do Not Use Workflow for Every Background Task
Workflow has visible state, persistence, operational UI, and process semantics.
Those capabilities also create overhead.
A task such as:
Rebuild this search document asynchronously.
does not automatically need a workflow.
If there is no human process, no meaningful workflow state, and no reason for operators to manage a process instance, Sling Jobs may be the cleaner tool.
Likewise:
Run cleanup every night.
is usually a scheduling/background-processing problem, not a workflow merely because the work has several Java methods.
Use Workflow when the process itself matters.
A Practical Approval Architecture
Consider this requirement:
Product content must be validated, reviewed by legal, marked approved, and then synchronized with an external commerce system.
I would separate it like this.
Step 1 --- Submission
The application validates that the payload is eligible to enter the approval process and starts the workflow.
Step 2 --- Automated Validation
A small WorkflowProcess delegates to:
productValidationService
.validate(payloadPath);
If business validation fails, the workflow follows the defined rejection/error route rather than hiding the result inside logs.
Step 3 --- Legal Participant
The workflow assigns a work item to the appropriate reviewer/group.
The process waits because human state is part of the requirement.
Step 4 --- Approval Route
Approval and rejection are visible branches in the model.
Step 5 --- Approved State
A small process step delegates to:
productStatusService
.markApproved(payloadPath);
Step 6 --- External Synchronization
The workflow creates a Sling Job containing the stable identifier needed for synchronization.
The job calls:
commerceSyncService
.synchronize(productId);
and owns technical retry behavior.
The workflow does not become the external API retry engine.
This architecture gives each mechanism one clear responsibility.
Production Failure: Workflow Is Stuck on a Custom Step
I check:
- Which workflow instance and work item are affected?
- Which custom process label/model step is executing?
- Is the OSGi process component active?
- Is the payload still valid?
- Is required OSGi configuration available?
- Can the service identity access the required repository path?
- Did the delegated service throw a business, configuration, or technical failure?
- Has the step already produced any side effects?
The last question matters before manually retrying or restarting anything.
Production Failure: Workflow Works Locally but Not in Another Environment
The workflow model may be identical while its dependencies are not.
Check:
- OSGi configuration
- Service-user mapping
- Repository permissions
- Participant groups
- Content/configuration paths
- External integration configuration
- Custom process component activation
A workflow model is only one part of the runtime design.
Production Failure: Duplicate Workflow Instances
Trace the start boundary.
Check:
- Workflow launcher conditions
- Custom code that starts the model
- Repeated author actions
- Event-driven triggers
- Whether an active instance check exists when the business requires one
- Whether the same logical content can appear under different paths/identifiers
Do not solve duplicate workflow instances only by detecting them at the final approval step.
Prevent them where the process begins when possible.
Production Failure: Approval Completed but Downstream System Is Not Updated
Separate workflow state from integration state.
Questions:
- Did the approval route complete?
- Did the integration handoff occur?
- Was a Sling Job created?
- Did the job execute?
- Did the external call fail temporarily or permanently?
- Is retry occurring?
- Could the same synchronization run twice safely?
- Is there reconciliation for work that never completed?
This is much easier to troubleshoot when the workflow does not hide the external integration inside one large process step.
Testing Strategy
I split workflow testing by responsibility.
Service Tests
Most business behavior should be testable without workflow objects.
For example:
productStatusService
.markApproved(path);
can be tested using the repository/service test approach appropriate to the implementation.
Process Adapter Tests
For a custom WorkflowProcess, test:
- Valid payload delegates correctly.
- Missing payload fails safely.
- Invalid payload type/path is rejected.
- Required process arguments are interpreted correctly.
- Delegated service failure becomes the intended workflow failure.
Participant Selection Tests
Test the rule that resolves the participant, not the AEM Inbox UI itself.
Integration Tests
In an AEM environment, verify:
- Model deployment
- Process component resolution
- Participant assignment
- Approval/rejection routes
- Repository permissions
- Workflow launcher behavior if used
- Handoff to jobs/integrations
- Behavior when payload changes while waiting
A unit test that mocks WorkItem successfully does not prove the
workflow model is wired correctly.
Observability
For production support, log identifiers that allow one process to be followed without dumping sensitive metadata.
Useful context may include:
- Workflow instance ID
- Workflow model identifier
- Payload path or business identifier
- Current custom step
- Downstream job identifier/business key
- Failure category
Avoid logs that only say:
Workflow failed
The operator needs enough context to find the instance and determine which boundary failed.
Architect Review Checklist
Before approving a workflow design, I review the model together with the services, repository identities, triggers, and downstream work it depends on:
Area Question
Process boundary Does this requirement genuinely need Workflow?
Model Are meaningful business stages visible in the model?
Payload Is the expected payload contract explicit?
Human state Which steps wait for people and who owns assignment?
Business logic Is reusable behavior behind services rather than buried in process classes?
Repository access Which service identity performs background operations?
Failure Which failures are business/permanent and which are transient?
Retry Does retryable technical work belong in a Sling Job?
Idempotency What happens if a step or downstream operation runs twice?
Concurrency Can multiple workflows act on the same payload?
Long-running state What happens if content moves, changes, or disappears while waiting?
Launchers Can repository changes start duplicate or recursive workflows?
Deployment Are model dependencies deployed and configured consistently?
Operations Can support teams identify, suspend, resume, or terminate the process safely?
Audit Is workflow history sufficient for the actual audit requirement?
A workflow model can look simple in the editor while hiding most of these decisions.
The architecture review should include the runtime behavior around the model, not just the boxes and transitions.
Summary
AEM Workflow is strongest when it owns a process rather than all of the code executed by that process.
The model should make meaningful business stages visible.
Participant steps should represent human interaction.
Custom process steps should remain thin adapters around reusable services.
Repository access should follow the same service-user and resolver-lifecycle rules used elsewhere in AEM.
Retryable technical integrations can be handed to Sling Jobs instead of turning workflow steps into retry engines.
Long-running workflows must also account for payload changes, duplicate instances, model evolution, launcher behavior, and operational recovery.
The useful architectural boundary is simple:
Workflow owns orchestration and process state. Services own business behavior. Jobs own durable asynchronous technical work.
What's Next
Chapter 32 — Workflow Models, Steps & Payload Design
The next chapter goes deeper into workflow-model construction: step selection, payload contracts, metadata, routes, participant behavior, model maintainability, and the design decisions that keep workflow models understandable as they grow.
Enjoyed this chapter?
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.