Source files
  • DataManagementModelsStandard/Workflow/WorkFlow.cs
  • DataManagementModelsStandard/Workflow/WorkFlowStep.cs
  • DataManagementModelsStandard/Workflow/WorkFlowAction.cs
  • DataManagementModelsStandard/Workflow/WorkFlowTrigger.cs
  • DataManagementModelsStandard/Workflow/WorkFlowRetryPolicy.cs
  • DataManagementEngineStandard/Editor/WorkFlowEditor.cs

Overview

The Workflow Engine provides a flexible system for defining and executing multi-step business processes. Workflows consist of steps connected in a directed graph, with each step containing actions, rules, and transitions.

The engine supports:

  • Directed graphs — Steps with previous/next references (not just linear)
  • Conditional branching — Route execution based on rule evaluation
  • Parallel execution — Multiple branches executing simultaneously
  • Parameter passing — Data flow between steps via InParameters/OutParameters
  • Error handling — Step-level retry policies with exponential backoff
  • Triggers — Manual, scheduled, event-driven, webhook, pipeline completion

WorkFlow Class

public class WorkFlow : IWorkFlow
{
    public List<IWorkFlowStep> Datasteps { get; set; }
    public List<string> DataSources { get; set; }
    public string WorkSpaceDatabase { get; set; }
    public string WorkSpaceFolder { get; set; }
    public string DataWorkFlowName { get; set; }
    public string Description { get; set; }
    public int ID { get; set; }
    public string GuidID { get; set; }
    public List<WorkFlow_Run_Result> workFlow_Run_Results { get; set; }

    public event EventHandler<WorkFlowEventArgs> WorkFlowStarted;
    public event EventHandler<WorkFlowEventArgs> WorkFlowEnded;
    public event EventHandler<WorkFlowEventArgs> WorkFlowRunning;
}

Properties

PropertyTypeDescription
DatastepsList<IWorkFlowStep>Step nodes in the workflow graph
DataSourcesList<string>Datasource names used by steps
WorkSpaceDatabasestringWorkspace database for intermediate storage
DataWorkFlowNamestringUnique workflow identifier
workFlow_Run_ResultsList<WorkFlow_Run_Result>Execution history

WorkFlowStep

public class WorkFlowStep : IWorkFlowStep
{
    public int Seq { get; set; }
    public string ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string StepType { get; set; }
    public string Code { get; set; }
    public bool IsFinish { get; set; }
    public bool IsRunning { get; set; }

    // Graph navigation
    public IWorkFlowStep PrevStep { get; set; }
    public List<IWorkFlowStep> NextStep { get; set; }

    // Actions to execute in this step
    public List<IWorkFlowAction> Actions { get; set; }

    // Parameter flow
    public List<IPassedArgs> InParameters { get; set; }
    public List<IPassedArgs> OutParameters { get; set; }

    // Rules for conditional execution
    public List<IWorkFlowRule> Rules { get; set; }

    public event EventHandler<WorkFlowEventArgs> WorkFlowStepStarted;
    public event EventHandler<WorkFlowEventArgs> WorkFlowStepEnded;
    public event EventHandler<WorkFlowEventArgs> WorkFlowStepRunning;
}

Step Properties

PropertyDescription
SeqSequence number for ordering
IsFinishTerminal step marker (end of workflow)
PrevStepSingle predecessor in graph
NextStepMultiple successors (forking)
ActionsActions executed when step runs
InParametersInput data from previous step
OutParametersOutput data for next step
RulesConditional execution rules

WorkFlowAction

public class WorkFlowAction : IWorkFlowAction
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string ActionTypeName { get; set; }
    public string Code { get; set; }
    public string ClassName { get; set; }
    public IDMEEditor DMEEditor { get; set; }

    // Action chain
    public IWorkFlowAction PrevAction { get; set; }
    public List<IWorkFlowAction> NextAction { get; set; }

    // Parameter flow
    public List<IPassedArgs> InParameters { get; set; }
    public List<IPassedArgs> OutParameters { get; set; }

    // Conditional rules
    public List<IWorkFlowRule> Rules { get; set; }
    public bool IsFinish { get; set; }
    public bool IsRunning { get; set; }

    // Execution
    public virtual PassedArgs PerformAction(
        IProgress<PassedArgs> progress, CancellationToken token);
    public virtual PassedArgs PerformAction(
        IProgress<PassedArgs> progress, CancellationToken token,
        Func<PassedArgs, object> actionToExecute);
    public virtual PassedArgs StopAction();
}

WorkFlowTrigger

public enum TriggerKind 
{ 
    Manual, 
    Scheduled, 
    EventDriven, 
    WebHook, 
    PipelineCompletion 
}

public class WorkFlowTrigger
{
    public TriggerKind Kind { get; set; } = TriggerKind.Manual;
    public string? CronExpression { get; set; }
    public string? EventTopic { get; set; }
    public string? PipelineId { get; set; }
}

Trigger Kinds

KindConfig FieldsExample
ManualNoneUser clicks "Execute"
ScheduledCronExpression"0 0 * * *" (daily midnight)
EventDrivenEventTopic"orders.created"
WebHookEventTopic (URL)"/api/hooks/slack"
PipelineCompletionPipelineId"daily-sync"

WorkFlowRetryPolicy

public class WorkFlowRetryPolicy
{
    public static WorkFlowRetryPolicy None { get; } = new() { MaxRetries = 0 };
    public static WorkFlowRetryPolicy Once { get; } = new() { MaxRetries = 1, DelaySeconds = 5 };

    public int MaxRetries { get; set; } = 0;
    public double DelaySeconds { get; set; } = 5;
    public bool ExponentialBackoff { get; set; } = false;
    public double MaxDelaySeconds { get; set; } = 300;

    public TimeSpan GetDelay(int attempt)
    {
        if (!ExponentialBackoff)
            return TimeSpan.FromSeconds(DelaySeconds);

        var delay = DelaySeconds * Math.Pow(2, attempt);
        return TimeSpan.FromSeconds(Math.Min(delay, MaxDelaySeconds));
    }
}

Backoff Examples

AttemptFixed (5s)Exponential (2s base)
15s2s
25s4s
35s8s
45s16s
55s32s

Workflow Rules

Rules enable conditional execution, branching, and validation within workflow steps and actions:

public interface IWorkFlowRule
{
    string RuleName { get; set; }
    bool Evaluate(IPassedArgs context);
}

// Conditional execution based on step outcome
step.Rules.Add(new WorkFlowRule
{
    RuleName = "CheckIfApproved",
    Evaluate = context => context.ParameterInt1 == 100
});

WorkFlowEditor

public class WorkFlowEditor
{
    public WorkFlowEditor(IDMEEditor editor);

    // Execution
    public Task<WorkFlowResult> ExecuteAsync(
        WorkFlow workflow,
        WorkFlowContext context);

    // Design-time
    public void AddStep(WorkFlow workflow, WorkFlowStep step);
    public void LinkSteps(WorkFlowStep from, WorkFlowStep to);
    public bool ValidateWorkflow(WorkFlow workflow, out List<string> errors);
}

WorkFlowResult

PropertyDescription
SuccessAll steps completed successfully
FailedStepName of the failing step (if any)
DurationTotal execution time
ErrorException details if failed
StepResultsPer-step outcome details

Conditional Branching

// Setup branching workflow
var validateStep = new WorkFlowStep { Name = "ValidateOrder", Seq = 1 };
var approveStep = new WorkFlowStep { Name = "ApproveOrder", Seq = 2 };
var rejectStep = new WorkFlowStep { Name = "RejectOrder", Seq = 2 };

// Branch based on validation result
validateStep.NextStep = new List<IWorkFlowStep> { approveStep, rejectStep };

approveStep.Rules.Add(new WorkFlowRule
{
    RuleName = "OrderApproved",
    Evaluate = ctx => ctx.ParameterInt1 == 100
});

rejectStep.Rules.Add(new WorkFlowRule
{
    RuleName = "OrderRejected", 
    Evaluate = ctx => ctx.ParameterInt1 != 100
});

// Only execute branch whose rules evaluate to true
// Rules provide flexible branching without hard-coded logic

Parallel Execution

var mainStep = new WorkFlowStep { Name = "ProcessOrder", Seq = 1 };

// Multiple parallel branches
var paymentStep = new WorkFlowStep { Name = "ProcessPayment", Seq = 2 };
var inventoryStep = new WorkFlowStep { Name = "UpdateInventory", Seq = 2 };
var notifyStep = new WorkFlowStep { Name = "SendNotification", Seq = 2 };

// Fork: one predecessor, multiple successors
mainStep.NextStep = new List<IWorkFlowStep> { paymentStep, inventoryStep, notifyStep };

// Each runs independently (parallel)
paymentStep.PrevStep = mainStep;
inventoryStep.PrevStep = mainStep;
notifyStep.PrevStep = mainStep;

// Join back to single step
var completeStep = new WorkFlowStep { Name = "CompleteOrder", Seq = 3, IsFinish = true };
paymentStep.NextStep.Add(completeStep);
inventoryStep.NextStep.Add(completeStep);
notifyStep.NextStep.Add(completeStep);

Usage Examples

Order Processing Workflow

var workflow = new WorkFlow
{
    DataWorkFlowName = "OrderProcessing",
    Description = "End-to-end order processing",
    Datasteps = new List<IWorkFlowStep>
    {
        new WorkFlowStep
        {
            Name = "ValidateOrder", Seq = 1,
            Actions = { new ValidateOrderAction() },
            NextStep = new List<IWorkFlowStep>()
        },
        new WorkFlowStep
        {
            Name = "ProcessPayment", Seq = 2,
            Actions = { new ProcessPaymentAction() },
            NextStep = new List<IWorkFlowStep>()
        },
        new WorkFlowStep
        {
            Name = "FulfillOrder", Seq = 3, IsFinish = true,
            Actions = { new FulfillOrderAction() }
        }
    },
    Triggers = new List<WorkFlowTrigger>
    {
        new WorkFlowTrigger { Kind = TriggerKind.EventDriven,
            EventTopic = "orders.created" },
        new WorkFlowTrigger { Kind = TriggerKind.Scheduled,
            CronExpression = "0 */6 * * *" } // Every 6 hours
    }
};

// Link steps
workflow.Datasteps[0].NextStep.Add(workflow.Datasteps[1]);
workflow.Datasteps[1].PrevStep = workflow.Datasteps[0];
workflow.Datasteps[1].NextStep.Add(workflow.Datasteps[2]);
workflow.Datasteps[2].PrevStep = workflow.Datasteps[1];

Execute with Context

var engine = new WorkFlowEditor(editor);
var result = await engine.ExecuteAsync(workflow, new WorkFlowContext
{
    Data = order,
    Parameters = new Dictionary<string, object>
    {
        ["UserId"] = userId,
        ["Source"] = "web",
        ["Priority"] = "high"
    }
});

if (result.Success)
{
    Console.WriteLine($"Completed in {result.Duration}");
    foreach (var step in result.StepResults)
        Console.WriteLine($"  {step.StepName}: OK");
}
else
{
    Console.WriteLine($"Failed at: {result.FailedStep}");
    Console.WriteLine($"Error: {result.Error?.Message}");
}

Custom Action

public class ValidateOrderAction : WorkFlowAction
{
    public override PassedArgs PerformAction(
        IProgress<PassedArgs> progress, CancellationToken token)
    {
        return PerformAction(progress, token, args =>
        {
            var order = InParameters[0].ParameterObject as Order;
            if (order.Total <= 0)
            {
                args.Messege = "Invalid order total";
                args.ParameterInt1 = -1;
            }
            else
            {
                args.Messege = "Order validated";
                args.ParameterInt1 = 100;
            }
            return args;
        });
    }
}

Validation

var editor = new WorkFlowEditor(dmeEditor);
if (!editor.ValidateWorkflow(workflow, out var errors))
{
    Console.WriteLine("Workflow validation failed:");
    foreach (var error in errors)
        Console.WriteLine($"  - {error}");
    return;
}

var result = await editor.ExecuteAsync(workflow, context);