Flow Node
The Flow node is the main orchestration component where execution starts in Wolf DSL. It defines the sequence of execution and supports conditional transitions based on data and expressions, allowing you to create dynamic execution paths.
Syntax
Flow [flowName] {
Start [startNode] {
transition {
<transition_body>
}
}
[node] {
transition {
<transition_body>
}
}
[node] {}
}
Parameters
flowName: The name of the flowstartNode: The initial state where execution beginsnode: Subsequent states in the flow execution path
Referenced Node Types
Flow nodes can reference any of these node types:
- Service - REST and GraphQL API calls
- Mapping - Data transformation logic
- Schema - Data structure definitions
- Value - Static and dynamic data
- GraphQLService - GraphQL-specific service calls
Transitions
Transitions define how execution moves between nodes. They are optional - if omitted, the flow terminates at that node.
Single Node Transition
The simplest transition always moves to one specific node:
transition {
nextNode
}
Example:
Flow userProcessingFlow {
Start userData {
transition {
validateUser
}
}
validateUser {
transition {
transformUser
}
}
transformUser {}
}
Conditional Transitions (Ternary)
Use conditional expressions for binary decision points:
transition {
condition ? trueNode : falseNode
}
Example:
Flow conditionalFlow {
Start userRequest {
transition {
userRequest.useCache == true ? getCachedUser : fetchUserFromAPI
}
}
getCachedUser {}
fetchUserFromAPI {}
}
Multi-way Branching (When-Then)
For complex decision trees with multiple conditions:
transition {
when [condition1] then [node1]
when [condition2] then [node2]
when [condition3] then [node3]
otherwise do [defaultNode]
}
Each condition is evaluated sequentially. The first condition that evaluates to true determines the transition. If no conditions match, the otherwise clause provides the default path.
Example:
Flow priorityBasedFlow {
Start request {
transition {
when request.priority == "high" then highPriorityHandler
when request.priority == "medium" then mediumPriorityHandler
when request.urgency == "critical" then emergencyHandler
otherwise do standardHandler
}
}
highPriorityHandler {}
mediumPriorityHandler {}
emergencyHandler {}
standardHandler {}
}
Complex Examples
Service Orchestration Flow
Schema UserRequest {
string userId
boolean useCache
}
Schema User {
string id
string name
string email
boolean isActive
}
Flow userServiceFlow {
Start userRequest {
transition {
userRequest.useCache == true ? getCachedUser : fetchUserFromAPI
}
}
getCachedUser {
transition {
when User.isActive == true then enrichUserData
when User.isActive == false then deactivateUser
otherwise do logError
}
}
fetchUserFromAPI {
transition {
enrichUserData
}
}
enrichUserData {}
deactivateUser {}
logError {}
}
Error Handling Flow
Flow resilientFlow {
Start inputData {
transition {
@Config("primary.service.enabled") == "true" ? primaryService : fallbackService
}
}
primaryService {
transition {
when @Config("transform.enabled") == "true" then transformData
otherwise do outputData
}
}
fallbackService {
transition {
transformData
}
}
transformData {
transition {
outputData
}
}
outputData {}
}
Data Processing Pipeline
Flow dataProcessingPipeline {
Start rawData {
transition {
when rawData.format == "json" then parseJSON
when rawData.format == "xml" then parseXML
when rawData.format == "csv" then parseCSV
otherwise do handleUnsupportedFormat
}
}
parseJSON {
transition { validateData }
}
parseXML {
transition { validateData }
}
parseCSV {
transition { validateData }
}
validateData {
transition {
validationResult.isValid == true ? transformData : handleValidationError
}
}
transformData {
transition { saveData }
}
saveData {}
handleUnsupportedFormat {}
handleValidationError {}
}
Best Practices
1. Clear Naming
Use descriptive names for flows and nodes:
// Good: Descriptive names
Flow userRegistrationFlow {
Start userRegistrationRequest {
transition { validateUserInput }
}
validateUserInput {
transition { createUserAccount }
}
createUserAccount {}
}
// Avoid: Generic names
Flow flow1 {
Start data {
transition { node1 }
}
node1 {}
}
2. Logical Grouping
Group related processing steps together:
// Good: Logical flow progression
Flow orderProcessingFlow {
Start orderRequest {
transition { validateOrder }
}
validateOrder {
transition { calculatePricing }
}
calculatePricing {
transition { processPayment }
}
processPayment {
transition { fulfillOrder }
}
fulfillOrder {}
}
3. Error Handling
Always plan for failure scenarios:
// Good: Includes error handling
Flow robustFlow {
Start request {
transition {
@Config("service.available") == "true" ? primaryService : fallbackService
}
}
primaryService {
transition {
response.success == true ? processResult : handleError
}
}
fallbackService {}
processResult {}
handleError {}
}
4. Avoid Deep Nesting
Keep transition logic readable:
// Good: Clear, flat structure
Flow userFlow {
Start user {
transition {
when user.type == "admin" then adminHandler
when user.type == "premium" then premiumHandler
when user.type == "basic" then basicHandler
otherwise do defaultHandler
}
}
adminHandler {}
premiumHandler {}
basicHandler {}
defaultHandler {}
}
// Avoid: Deeply nested conditions
Flow complexFlow {
Start data {
transition {
data.type == "A" ? (data.subtype == "1" ? nodeA1 : nodeA2) : (data.type == "B" ? nodeB : nodeDefault)
}
}
// This becomes hard to read and maintain
}
Execution Model
Wolf DSL flows execute with these characteristics:
- Single Entry Point: Every flow starts at the
Startnode - Deterministic: Given the same input, flows produce consistent results
- Conditional Branching: Runtime decisions based on data and configuration
- Terminal Nodes: Nodes without transitions end the flow execution
- Type Safety: All referenced nodes must exist and be properly typed
Related Topics
- Service Node - Making HTTP and GraphQL calls
- Mapping Node - Data transformation and manipulation
- Expression System - Writing conditions and calculations
- Installation Guide - Setting up configuration and environment
Flow nodes are the backbone of Wolf DSL, orchestrating complex business processes through simple, declarative configuration.