Designing a Flexible Workflow Engine: From Simple Chains to Complex Nested Nodes
This article narrates the step‑by‑step evolution of a custom workflow engine, detailing how simple sequential approvers were transformed into a tree‑structured system supporting parallel, multi‑sign, conditional, delegation, and nested nodes, complete with status management and progress metrics.
Initially, the boss requested a simple workflow engine, leading to a basic linked‑list design where approvers are added sequentially, the current approver moves forward after approval, and the process ends when the final node is reached.
Level 2 – Adding Multi‑Sign Nodes
To support multi‑sign nodes, the design shifted from a linear list to a tree structure, distinguishing simple nodes (single approver) and complex nodes (containers for sub‑nodes). Multi‑sign nodes activate all child nodes, completing only when every child is approved.
Level 3 – Introducing Parallel Nodes
Parallel nodes were added as complex nodes where any child’s approval completes the node; a new "Skip" status was defined to disable sibling nodes once a parallel path succeeds.
Level 4 – Unlimited Nesting
The tree‑based architecture naturally allowed unlimited nesting of any node type, enabling arbitrarily complex approval flows.
Level 5 – Conditional Nodes
Conditional nodes were introduced, functioning like parallel nodes but activating only those child branches whose conditions, based on form data, are satisfied.
Level 6 – Dynamic Approver Types
Simple nodes were expanded into three categories: fixed approvers, approvers derived from form fields, and approvers computed via a mapping function (e.g., get_manager("User")).
Level 7 – Rejection to Initiator
A rejection feature was added, allowing Ready‑state nodes to reject back to the initiator, effectively restarting the workflow.
Level 8 – Rejection to Previous Approver
Logic was implemented to reject to the immediate previous approver, handling the complexity of nested structures to locate the correct prior node.
Level 9 – Rejection to Arbitrary Node
The system now supports rejecting to any node by iteratively moving up the tree until a Ready node containing the target is found.
Level 10 – Time‑Limited Nodes
Nodes can now have a time limit; if not completed within the allotted period, they are marked as timed‑out.
Level 11 – Delegation
Delegation is modeled by creating a parallel parent node with a sibling representing the delegate, allowing both the original approver and delegate to complete the task; delegation can be nested indefinitely.
Level 12 – Cancel Delegation
Cancellation of delegation is implemented as the inverse operation, prohibited if the delegate has already approved.
Level 13 – Pre‑ and Post‑Conditions
Each node can now define pre‑conditions (required to enter the node) and post‑conditions (required to mark the node as complete), adding further control to the workflow.
Level 14 – Progress Metric
A progress percentage is calculated by measuring the distance from the leftmost node to the rightmost Ready node relative to the total width of the tree.
Level 15 – Execution Scripts
Two scripts can be attached to each node: one executed when the node starts approval and another when the node finishes approval.
The narrative concludes with a personal reflection on the demanding nature of these iterative requirements and the eventual commercial success of the workflow system.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.