What is a flow?
The program file in Codeflow is called as a flow. A flow typically takes an input, do some processing and produces an output. A flow consists of one or more steps connected together through transitions. A flow typically starts the execution from the
start step, processing each step by following the outgoing transitions until it reaches the
end step. A flow can invoke other flows and are typically composed together to create complex business logic.
The boxes in a flow are called as the steps or activities. Steps are the basic unit of processing in Codeflow. They are represented as movable boxes in the graph. Steps are typically created by dragging & dropping modules from the packages panel. The steps are executed in an asynchronous fashion. The output of a step once obtained can never be altered. A flow typically consists of multiple steps placed meaningfully and connected together using transitions.
The connection between two steps is called as a transition. Transitions represents the direction of the program flow. This is unlike few common Visual programming languages where transitions are used for mapping multiple input fields of each step. In Codeflow the input data mapping is abstracted into a step's input tab. This makes the flow appear clean and lets developers understand the high level control flow in a glance.
After a step is executed, the engine follows the outgoing transitions to pick the next set of steps to be invoked. There are different types of transitions that can determine the control flow by having conditions that specify whether the transition should occur or not during the runtime and how should it react during exceptions, etc.
Modules are the reusable building blocks in Codeflow package library. A module when placed in a flow becomes a step or an activity in that flow. Codeflow ships with dozens of useful, standard packages that comes with a set of modules that provides with core building blocks of the language. Third party modules can be installed through the integrated package manager in the Codeflow designer.
A flow is internally stored as a JSON file with the details of the steps and transitions created through the designer. A flow file is typically stored with an extension
Below is how a sample flow file looks like when opened as a raw text file.
//activities contains list of modules placed in the flow
"name": "Step 1",
"ref": "module-path" //Unique reference to the module
//transitions array holds the details of the transitions between the steps
And this is how the same flow would look when opened in the designer.
How does a flow work?
The execution of a flow is easy to visualize. For regular flows, the execution starts from the
start step, and follows the outgoing transitions, executing all the connected steps until it reaches the
end step. Before executing a step, its input data mapping is evaluated using the current state and the data is passed as input into the step for processing. After executing a step, the output is stored in the current stack under the name given to the step.
end step is reached, the control is passed back to the parent flow with the data mapped in the end step's input. There can be multiple
end steps but only one
start step. Steps that are not connected to the
end step will continue to execute even after the flow is returned. This allows for performing any processing that are not required to be completed before returning back to the parent flow, like cleaning log files etc.
Even though every step in a flow is executed asynchronously, the flow abstracts away the "callback hell" that is synonymous with asynchronous programming, leaving a beautiful graph that is much more readable. Flow can also represent complex, asynchronous joins and business logics which are otherwise difficult to represent in raw, text-based programming. It is explained in detail in the chapter - control flow.
Types of flows
There are two types of flow:
Regular flows - Contains a
startstep and an
endstep. Regular flows are invoked from other flows by passing an input and getting back an output. Regular flows can also be invoked from the designer for testing purposes.
Trigger flows - Trigger flows are special type of flows that creates a new instance of itself after an external trigger. The trigger could be any event, like a timer that gets triggered during every configured interval or a TCP event that gets triggered when a request comes.
Regular flows are the default type of flows with a
start and an
end step. Regular flows can define its input and output schema in the
start and the
end step respectively.
Unlike trigger flows, regular flows do not start by itself. They are invoked manually by the user or called from other flows. The ability to invoke a flow from another flow makes it possible to build complex business logic by composing several smaller flows.
Trigger flows are created when a trigger module is added into the flow. A trigger module, when added, will automatically replace the
start step and becomes the first step in a flow. A trigger flow, when started, will start listening for the event(s) it was designed and configured for. It will then trigger a new instance of the flow every time an instance of that event occurs.
The below animation shows an example of the
core/timer trigger module used to trigger a new instance of flow during the configured time interval, in the below case, every one second (1000 milliseconds).
Trigger flows are self started and cannot be invoked from within another flow. When a project is run as a whole, all the trigger flows under the project are automatically started. A trigger flow will continue running until the engine is stopped. Trigger flows can be tested from the designer just like regular flows using the run and debug buttons.
Using flow in the designer
Even though flows are represented as plain JSON files, they are typically edited using the Codeflow designer using the graphical editor. Flows are created by choosing New File->Flow.. from the File menubar or by right-clicking the file area and choosing the option
A new flow by default will only have a
start and an
end step. As per the desired business logic, modules are placed from the package panel into the canvas and wired together with transitions. Highlighting a step or transition will bring the configuration panel, which shows all the options to configure them.
Configuring a step
Clicking on a step brings reveals the configuration panel, which contains several tabs dedicated for configuring a step/module. The Information tab gives a high level description of the flow. You can also rename the step by changing the name field here. The Input tab is where you can map the input data required for the flow. The output tab shows the output that the step will have when it finishes execution.
Every step in a flow has an input form. The input fields lets you enter expressions and can be mapped using the output data from the connected steps before it. Input mapping is explained in detail in the section - input mapping.
Few modules like
mapper will let you define an input schema. Such modules makes it easy for user to define the desired data structure. The input section of a step automatically reflects the input schema and lets you map the data as per the schema's structure.
The output tab shows the output data schema of each module. It is read only and useful for understanding the output data structure of a step.
The connection between two steps is called as a transition. Transitions are created by dragging and dropping an arrow from the source step to the target step. Transitions are also automatically created when dropping modules over existing steps.
Transitions determine the control the flow during the runtime. By changing the way transitions are placed, it is possible to make steps execute in parallel or in a sequential fashion. Transitions can also change their behavior during the runtime. For e.g., you can specify an optional rule that will evaluate during the runtime that determines if the transition should occur or not. If a transition does not occur, all the steps beneath that will also be skipped, unless they are connected by one or more other active transitions. This makes it easy for complex business logic to be represented in a flow, which is otherwise difficult to visualize in regular code.
Types of transitions
A transition's type can be changed by selecting it and choosing the type from the drop down. Transitions are broadly classified into two:
- Success transitions - will occur only when there is no error thrown by the source step.
- Exception transitions - will occur when there is an error thrown by the source step.
A transition under success type will be taken only if there are no errors thrown by the source. There are three subtypes for success transitions:
Default - This is the default transition type when creating a connection. The connection is triggered whenever the source step complete successfully with no error.
Conditional - The conditional transition allows an expression that will be evaluated in runtime to determine whether the transition should occur. This is the primary way to control the flow of execution. Any boolean expression can be used for the condition. The transition happens only if the condition evaluates to true (And there are no errors, in which exception transitions, if any are taken).
Otherwise - This a special type of transition supplementing conditional transitions. It's analogous to the else statement in traditional programming languages. The otherwise transition wil occur only if there are no other transition evaluated to true in the runtime.
Exception transitions are the way to handle errors in Codeflow. When an error occurs in a step, the engine will check if there are any error or always transitions attached to it. If there is, then they are taken. If there are no error or always transition available, the error will bubble up through the parent flow(s).
Error - This transition type will occur when there is an error. It is possible to have an error transition in parallel to a success transition to handle exceptions on a separate path.
Always - The Always transition will happen no matter what. This is the way to handle both error and successful outcomes in a single transition. It could be seen analogous to final in try/catch blocks in traditional programming languages.
Refer to the exception handling section for example application of Error transitions.
Flows can be composed together and it makes it easy to form complex business logic.
The core module
executeFlow can be used to invoke other flows. Once placed, the
executeFlow is like any other step. The flow field in its input form is supplemented with a convenient browse button to select a flow from a file tree. After a flow file is selected, the input and the output of the step changes to reflect the input and output of the flow chosen file.
Alternatively, flow files can also be dragged and dropped into the canvas to create an
executeFlow step with the flow field set automatically to the flow's path.
This chapter covered flows and their working. Subsequent chapters will expand further on the concepts discussed briefly here.