Skip to main content
Version: Next

SubflowActivity

Category: Common Steps Namespace: CMS.Plugin.FlowManagement.Abstractions.FlowBusiness.Activitys Base Class: BusinessActivity Module: FlowManagement.Abstractions

Overview​

SubflowActivity is a special node used to start and execute a subflow within a main flow. By specifying the flow ID of the subflow, another independent flow can be invoked during the execution of the main flow, enabling modularization and reuse of flows. The subflow node supports parameter passing between parent and child flows, allowing data from the main flow to be passed to the subflow, and results from the subflow to be returned to the main flow.

Subflows are an important pattern in flow design, allowing complex business logic to be decomposed into multiple independent, reusable flow modules, improving the maintainability and scalability of flows.

Business Scenarios​

Applicable Scenarios​

  • Flow Reuse: Encapsulate common business logic as subflows for reuse in multiple main flows
  • Flow Modularization: Decompose complex flows into multiple subflows to reduce the complexity of individual flows
  • Dynamic Flows: Dynamically select the subflow to execute based on business conditions
  • Standardized Operations: Encapsulate standardized operational flows as subflows to ensure consistency in execution
  • Flow Nesting: Support multi-level nested subflow calls

Role in the System​

SubflowActivity acts as a flow composer in the LMES flow system:

  • Starts and manages the execution of subflows
  • Handles parameter passing between parent and child flows
  • Maintains the relationship between parent and child flows
  • Supports modular flow design
  • Provides a flow reuse mechanism

Collaboration with Other Nodes​

  • BusinessActivity: Business nodes within the subflow execute business logic normally
  • Main Flow Nodes: SubflowActivity acts as a node in the main flow, collaborating with other nodes
  • Parameter Mapping: Data exchange between parent and child flows is implemented through Mapping configuration

Configuration Instructions​

Basic Configuration​

Property NameTypeRequiredDefault ValueDescription
ProcessflowIDintYes0Subflow ID, the flow ID of the subflow to be started
IsEmbeddedboolNotrueWhether to embed the subflow definition into the parent flow
MappingParameterMapCollectionNoEmpty CollectionParameter map, parameter mapping table for parameter passing between parent and child flows
ExtendedPropertyBusinessPropertyCollectionNoEmpty CollectionStep extended properties

Configuration Details​

ProcessflowID​

Description: The flow ID of the subflow, specifying the subflow to be started. This is the most important configuration item for the subflow node.

Value Range: Integer greater than 0, corresponding to a defined flow ID in the system

Notes:

  • Must ensure the specified flow ID exists in the system
  • The subflow must be a published flow
  • The flow ID can be dynamically specified through the flow context

IsEmbedded​

Description: Whether to embed the subflow definition into the parent flow. When set to true, the definition of the subflow will be embedded into the parent flow.

Value Range: true or false

Notes:

  • Defaults to true, recommended to use the default value
  • In embedded mode, modifications to the subflow will not affect running parent flow instances
  • In non-embedded mode, modifications to the subflow will affect all parent flows referencing it

Mapping​

Description: Parameter map, defining the parameter mapping relationship between parent and child flows. Through mapping configuration, data from the parent flow can be passed to the subflow, and results from the subflow can be returned to the parent flow.

Value Range: ParameterMapCollection object, containing multiple parameter mapping items

Mapping Item Structure:

  • SourceKey: Source parameter key (key name in the parent flow)
  • TargetKey: Target parameter key (key name in the subflow)
  • Direction: Mapping direction (Input/Output/Both)
    • Input: Passed from parent flow to subflow
    • Output: Returned from subflow to parent flow
    • Both: Two-way passing

Notes:

  • Parameter key names usually end with "_Value"
  • Mapping direction determines the timing of data passing
  • Multiple parameter mappings can be configured

Flow Context​

Input Parameters​

Parameter NameTypeDescription
Parent Flow DataanyData passed to the subflow through Mapping configuration
@ParentCasestringParent flow instance ID (automatically set by system)
@ParentTaskintParent flow corresponding task ID (automatically set by system)

Output Parameters​

Parameter NameTypeDescription
Subflow ResultanyData returned from the subflow through Mapping configuration
@SubflowCasestringSubflow instance ID (automatically set by system)

System Parameters​

SubflowActivity defines three system parameter constants:

  • ParameterParentCase (@ParentCase): Parent flow instance ID
  • ParameterParentTask (@ParentTask): Parent flow corresponding task ID
  • ParameterSubflowCase (@SubflowCase): Subflow instance ID

These parameters are automatically managed by the system and used to maintain the relationship between parent and child flows.

Data Flow Description​

  1. Before Starting Subflow:

    • According to Mapping configuration, copy parent flow data to subflow context
    • Set system parameters (@ParentCase, @ParentTask)
  2. Subflow Execution:

    • Subflow executes in an independent context
    • Can access parameters passed from the parent flow
  3. After Subflow Completion:

    • According to Mapping configuration, copy subflow results back to parent flow context
    • Set subflow instance ID (@SubflowCase)

Business Logic Description​

Processing Flow​

The execution flow of SubflowActivity is as follows:

  1. EnterAsync: Enter the subflow node

    • Create a new work item (FlowItem)
    • Set work item properties:
      • StartTime: Current time
      • TaskName: Node name
      • Alias: Node alias
      • PartName: Fixed as "System"
      • LevelCode: Inherit level code from parent flow
      • KeyInfo and KeyLabel: Obtained from current task or event parameters
    • Record parent flow information:
      • ParentCaseID: Parent flow instance ID
      • ParentInstanceName: Parent flow name
      • ParentTask: Parent flow task ID
    • Add work item to flow instance
    • Update current work item ID
    • Record entry log
  2. Start Subflow:

    • Load subflow definition based on ProcessflowID
    • Create subflow instance
    • Pass parameters based on Mapping configuration
    • Start subflow execution
  3. Wait for Subflow Completion:

    • Subflow executes in an independent thread or task
    • Main flow waits for subflow to complete
  4. Subflow Completion:

    • Get subflow results based on Mapping configuration
    • Write results to parent flow context
    • Continue execution of subsequent nodes in the parent flow

Flowchart​

[Main Flow Node 1]
↓
[SubflowActivity]
↓ (Start Subflow)
┌─────────────────┐
│ Subflow Start │
│ ↓ │
│ Subflow Node 1 │
│ ↓ │
│ Subflow Node 2 │
│ ↓ │
│ Subflow End │
└─────────────────┘
↓ (Subflow Complete)
[Main Flow Node 2]

Parameter Mapping Mechanism​

Execution timing of parameter mapping:

  • Input Mapping: Executed before starting the subflow

    Subflow Context[TargetKey] = Parent Flow Context[SourceKey]
  • Output Mapping: Executed after subflow completion

    Parent Flow Context[SourceKey] = Subflow Context[TargetKey]
  • Both Mapping: Executes both Input and Output mappings

Dependent Services​

Service InterfaceUsageDescription
FlowFlow InstanceAccess parent flow context and work items
Flow.LoggerLoggerRecord subflow execution logs
Flow EngineSubflow ManagementLoad and execute subflows

Exception Handling​

Exception handling in SubflowActivity:

  • Subflow Does Not Exist: Throws an exception if the specified ProcessflowID does not exist
  • Parameter Mapping Error: If the mapped parameter key does not exist, it may lead to data loss
  • Subflow Execution Failure: Exceptions in the subflow will propagate to the parent flow

Logging​

SubflowActivity records the following key logs:

  • Enter Node: Enter, TaskID={taskId}
  • Parent Flow Info: ParentCaseID, ParentInstanceName, ParentTask
  • Subflow Start: Subflow ID and instance information
  • Parameter Passing: Mapped parameter information

Usage Examples​

Basic Example​

{
"Name": "Main Flow",
"Activities": [
{
"Type": "BusinessActivity",
"Name": "Prepare Data",
"Alias": "PrepareData"
},
{
"Type": "SubflowActivity",
"Name": "Execute Inspection Subflow",
"Alias": "InspectionSubflow",
"ProcessflowID": 1001
},
{
"Type": "BusinessActivity",
"Name": "Process Result",
"Alias": "ProcessResult"
}
],
"Transitions": [
{
"From": "PrepareData",
"To": "InspectionSubflow"
},
{
"From": "InspectionSubflow",
"To": "ProcessResult"
}
]
}

Advanced Example: With Parameter Mapping​

{
"Type": "SubflowActivity",
"Name": "Quality Inspection Subflow",
"Alias": "QualityCheckSubflow",
"ProcessflowID": 2001,
"IsEmbedded": true,
"Mapping": [
{
"SourceKey": "ProductCode_Value",
"TargetKey": "Input_ProductCode_Value",
"Direction": "Input"
},
{
"SourceKey": "WorkOrder_Value",
"TargetKey": "Input_WorkOrder_Value",
"Direction": "Input"
},
{
"SourceKey": "CheckResult_Value",
"TargetKey": "Output_Result_Value",
"Direction": "Output"
},
{
"SourceKey": "CheckDetails_Value",
"TargetKey": "Output_Details_Value",
"Direction": "Output"
}
]
}

Complete Flow Example: Multi-level Nested Subflows​

{
"Name": "Main Production Flow",
"Activities": [
{
"Type": "BusinessActivity",
"Name": "Receive Order",
"Alias": "ReceiveOrder"
},
{
"Type": "SubflowActivity",
"Name": "Material Preparation Subflow",
"Alias": "MaterialPreparation",
"ProcessflowID": 3001,
"Mapping": [
{
"SourceKey": "WorkOrderNo_Value",
"TargetKey": "Input_OrderNo_Value",
"Direction": "Input"
},
{
"SourceKey": "MaterialReady_Value",
"TargetKey": "Output_Ready_Value",
"Direction": "Output"
}
]
},
{
"Type": "SubflowActivity",
"Name": "Production Execution Subflow",
"Alias": "ProductionExecution",
"ProcessflowID": 3002,
"Mapping": [
{
"SourceKey": "WorkOrderNo_Value",
"TargetKey": "Input_OrderNo_Value",
"Direction": "Input"
},
{
"SourceKey": "ProductionResult_Value",
"TargetKey": "Output_Result_Value",
"Direction": "Output"
}
]
},
{
"Type": "SubflowActivity",
"Name": "Quality Inspection Subflow",
"Alias": "QualityInspection",
"ProcessflowID": 3003,
"Mapping": [
{
"SourceKey": "ProductCode_Value",
"TargetKey": "Input_ProductCode_Value",
"Direction": "Input"
},
{
"SourceKey": "InspectionResult_Value",
"TargetKey": "Output_Result_Value",
"Direction": "Output"
}
]
},
{
"Type": "BusinessActivity",
"Name": "Complete Order",
"Alias": "CompleteOrder"
}
],
"Transitions": [
{
"From": "ReceiveOrder",
"To": "MaterialPreparation"
},
{
"From": "MaterialPreparation",
"To": "ProductionExecution"
},
{
"From": "ProductionExecution",
"To": "QualityInspection"
},
{
"From": "QualityInspection",
"To": "CompleteOrder"
}
]
}

Dynamic Subflow Selection Example​

// Dynamically select subflow based on conditions in the main flow
[Serializable]
[Design("Dynamic Subflow Selection", "Select different inspection subflows based on product type", Sort = 1)]
[Category("Custom")]
public class DynamicSubflowActivity : BusinessActivity
{
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// Get product type
var productType = Flow.DataItems["ProductType_Value"]?.ToString();

// Select subflow based on product type
int subflowId = productType switch
{
"TypeA" => 4001, // Type A product inspection flow
"TypeB" => 4002, // Type B product inspection flow
"TypeC" => 4003, // Type C product inspection flow
_ => 4000 // Default inspection flow
};

// Set subflow ID to flow context
Flow.DataItems["SelectedSubflowID_Value"] = subflowId;

Flow.Logger.LogMessage($"Selected Subflow: ProductType={productType}, SubflowID={subflowId}", Name);

await base.ProcessAsync(args);
}
}

Extension Guide​

Inheritance Hierarchy​

Activity (SYC.Flow.Kernel)
└── BusinessActivity
└── SubflowActivity

Overridable Methods​

Method NameUsageWhen to Override
EnterAsyncExecuted when entering the nodeNeed custom processing before subflow starts
ProcessAsyncCore business logicNeed to add custom logic before or after subflow execution
ExitAsyncExecuted when exiting the nodeNeed custom processing after subflow completion

Custom Subflow Node Example​

[Serializable]
[Design("Enhanced Subflow", "Subflow node with pre and post processing", Sort = 1)]
[Category("Custom")]
public class EnhancedSubflowActivity : SubflowActivity
{
[Design("Timeout", "Subflow execution timeout (seconds)", Sort = 1)]
[Category("Configuration")]
[DataMember]
public int TimeoutSeconds { get; set; } = 300;

[Design("Retry Count", "Retry count when subflow fails", Sort = 2)]
[Category("Configuration")]
[DataMember]
public int RetryCount { get; set; } = 3;

public override async Task EnterAsync(ProcessflowEventArgs e)
{
// Record subflow start time
Flow.DataItems["SubflowStartTime_Value"] = DateTime.Now;

// Set timeout monitoring
Flow.Logger.LogMessage($"Starting subflow, timeout: {TimeoutSeconds} seconds", Name);

await base.EnterAsync(e);
}

public override async Task ProcessAsync(ProcessflowEventArgs args)
{
int attempt = 0;
bool success = false;

while (attempt < RetryCount && !success)
{
attempt++;

try
{
Flow.Logger.LogMessage($"Executing subflow, attempt {attempt}", Name);

// Execute subflow
await base.ProcessAsync(args);

// Check subflow result
var result = Flow.DataItems["SubflowResult_Value"]?.ToString();
success = result == "Success";

if (!success && attempt < RetryCount)
{
Flow.Logger.LogWarningMessage($"Subflow execution failed, preparing to retry", Name);
await Task.Delay(1000); // Wait 1 second before retry
}
}
catch (Exception ex)
{
Flow.Logger.LogExceptionMessage(ex, Name);

if (attempt >= RetryCount)
{
throw;
}
}
}

if (!success)
{
throw new BusinessException(Name, $"Subflow execution failed after {RetryCount} retries");
}
}

public override async Task ExitAsync(ProcessflowEventArgs args)
{
// Calculate subflow execution duration
var startTime = (DateTime)Flow.DataItems["SubflowStartTime_Value"];
var duration = (DateTime.Now - startTime).TotalSeconds;

Flow.DataItems["SubflowDuration_Value"] = duration;
Flow.Logger.LogMessage($"Subflow completed, duration: {duration:F2} seconds", Name);

await base.ExitAsync(args);
}
}

Registration and Integration​

  1. Compile Node:

    • Inherit from SubflowActivity class
    • Add Design and Category attributes
    • Compile into DLL
  2. Deploy Node:

    • Place DLL in LMES plugin directory
    • Restart LMES service
  3. Use Node:

    • Select subflow node in flow designer
    • Configure subflow ID and parameter mapping
    • Connect to main flow

Notes​

  • ⚠️ Flow ID: Must ensure the subflow specified by ProcessflowID exists and is published
  • ⚠️ Parameter Mapping: Carefully configure parameter mapping to ensure correct key names and avoid data loss
  • ⚠️ Mapping Direction: Distinguish between Input, Output, and Both mapping directions
  • ⚠️ Circular Calls: Avoid circular subflow calls (A calls B, B calls A), which will lead to infinite recursion
  • ⚠️ Performance Impact: Subflows increase flow execution time, pay attention to performance optimization
  • ⚠️ Transaction Handling: Transactions of parent and child flows are independent, pay attention to transaction boundaries
  • ⚠️ Exception Propagation: Exceptions in subflows will propagate to the parent flow, ensure proper exception handling
  • 💡 Best Practices:
    • Encapsulate reusable business logic as subflows
    • Use clear parameter naming to facilitate understanding of data flow
    • Record detailed logs in subflows for troubleshooting
    • Consider using IsEmbedded=true to avoid subflow modifications affecting running instances
    • Set reasonable timeouts for subflows
    • Validate subflow execution results in the main flow

FAQ​

Q1: How to access parent flow data in a subflow?​

A: Configure parameter mapping (Mapping) to pass parent flow data to the subflow:

{
"Mapping": [
{
"SourceKey": "ParentData_Value",
"TargetKey": "ChildData_Value",
"Direction": "Input"
}
]
}

In the subflow, you can access it via Flow.DataItems["ChildData_Value"].

Q2: How to return subflow results to the parent flow?​

A: Use Output direction parameter mapping:

{
"Mapping": [
{
"SourceKey": "ParentResult_Value",
"TargetKey": "ChildResult_Value",
"Direction": "Output"
}
]
}

The subflow writes the result to Flow.DataItems["ChildResult_Value"], which is automatically copied to the parent flow's Flow.DataItems["ParentResult_Value"].

Q3: Can subflows be nested?​

A: Yes, subflows can call other subflows, forming multi-level nesting. However, note:

  • Avoid circular calls
  • Control nesting depth, as excessive nesting affects performance and maintainability
  • Each level of nesting requires parameter mapping configuration

Q4: How to dynamically select the subflow to execute?​

A: Add a business node before the subflow node to set the subflow ID based on conditions:

// In business node
Flow.DataItems["DynamicSubflowID_Value"] = selectedSubflowId;

// Use variable reference in subflow node
// ProcessflowID = {DynamicSubflowID}

Q5: Does subflow execution failure affect the parent flow?​

A: Yes. Exceptions in the subflow will propagate to the parent flow, causing the parent flow to fail as well. Recommendations:

  • Implement proper exception handling in the subflow
  • Catch subflow exceptions in the parent flow
  • Use custom subflow nodes to implement retry mechanisms

Q6: What is the use of the IsEmbedded property?​

A:

  • IsEmbedded=true (Default): The subflow definition is embedded into the parent flow; modifications to the subflow do not affect running parent flow instances.
  • IsEmbedded=false: The subflow definition is not embedded; all parent flows share the same subflow definition, and modifications to the subflow affect all parent flows referencing it.

It is recommended to use the default value true to ensure flow stability.

Q7: How to view the relationship between parent and child flows?​

A: You can view it through system parameters:

  • @ParentCase: Parent flow instance ID
  • @ParentTask: Parent flow task ID
  • @SubflowCase: Subflow instance ID

These parameters are automatically set by the system and can be queried in logs or databases.

Update History​

DateVersionDescription
2025-11-281.0Initial version

Last updated: 2025-11-28