You are reading the documentation for an outdated Corteza release. 2023.9 is the latest stable Corteza release.

Workflow Scope in Depth

A scope defines what data an ongoing step can access and manipulate.

For the most common use-cases this doesn’t really matter, but it can come in handy when defining larger and more complicated workflows.

Non-primitive data types (more can be found here) are passed around using pointers, which means that any change to one copy reflects to all of the copies.

To exemplify; a user is present in two different scopes. In case a change of name in one of the scopes is made, all of the scopes are affected.

The two expression steps interact with the same user:

object pointers

Gateways

When the workflow execution reaches a gateway, a new scope is created for each connector (branch) based on the parent scope (usually that is the global scope).

When the branches are joined (they reach the join gateway step), the scopes of all the branches are merged back into the parent scope in the initially defined order (all changes and new variables in the branch scopes are reflected in the parent scope).

The order in which the branches are merged is defined when you first define the connectors. Visually changing the order of the connectors doesn’t affect the merging logic.

gateway scope base
Figure 1. Let’s take a look at an example;
A couple of important steps can be seen in the above example:
  • Setting the global scope initializes the global_v1 variable to be 1.

  • Setting the local (branch 1) scope initializes the local_v1 variable to be 1.

  • Setting the local (branch 2) scope initializes the local_v2 variable to be 1.

  • Logging the scope logs the variables in the ongoing scope.

To break this down further, the below image shows the stack trace of the entire execution with annotated scopes at the important parts (darkened numbered blocks).

gateway scope

For this example we used the fork gateway.

  1. When the workflow starts, you initialize the ongoing (global) scope to have a variable global_v1 with the value of 1. When the gateway step is reached, two new scopes are created; one for each branch (branch 1 scope and branch 2 scope).

  2. When the first branch is reached, the workflow executes it using its scope (branch 1 scope).

  3. When you interact with the ongoing scope by initializing the local_v1 variable to the value of 1, only the ongoing scope is changed (the global state remains unchanged).

  4. Upon the reach of the second branch, the workflow executes it using its scope (branch 2 scope).

  5. When interacting with the ongoing scope by initializing the local_v2 variable to the value of 1, only the ongoing scope is changed (the global state remains unchanged).

  6. When both branches are executed and the join step is reached, both of the branches and their scopes are merged into the parent scope (in this case the global scope) in the initially defined order. If the first branch was "joined" before the second branch, the scope of the first branch is merged first.

Iterators

When the workflow execution reaches an iterator, a new scope is created based on the parent scope (usually the global scope).

When the iterator has finished executing, which means that all iterations are completed or the iteration was aborted, the iterator’s scope is merged back into the parent scope (all changes and new variables in the iterator scope are reflected in the parent scope).

loop scope base
Figure 2. Let’s take a look at an example:
A couple of important steps can be seen in the above example:
  • Setting the global scope initializes the global_v1 variable to be 1.

  • Iterator 1 repeats 1 time.

  • Setting the local (iterator 1) scope increments the global_v1 variable by 1 and initializes the local_v1 variable to be 1.

  • Iterator 2 repeats 1 time.

  • Setting the local (iterator 2) scope increments the global_v1 variable by 1 and initializes the local_v2 variable to be 1.

  • Logging the scope logs the variables from the ongoing scope.

To break this down further, the below image shows the stack trace of the entire execution with annotated scopes at the important parts (darkened numbered blocks).

loop scope
  1. When the workflow starts, you initialize the ongoing (global) scope to have a variable global_v1 with the value of 1.

  2. When you reach the first iterator, a new scope (iterator 1 scope) is prepared for this iterator based on the parent scope (global).

  3. When you interact with the ongoing scope by incrementing the global_v1 and by setting the local_v1 variable to the value of 1, only the ongoing scope is changed (the global state remains unchanged).

  4. When you reach the second iterator, a new scope (iterator 2 scope) is prepared for this iterator based on the parent scope (iterator 1 scope).

  5. When you interact with the ongoing scope by incrementing the global_v1 and by setting the local_v2 variable to the value of 1, only the ongoing scope is changed (the iterator 1 scope state remains unchanged).

  6. After the second iterator has finished executing (all of the iterations were handled), the scopes from the second and first iterator are merged. The ongoing scope (iterator 1 scope) is updated with the merged scope.

  7. After the first iterator has finished the execution (all of the iterations were handled), the global scope and the first iterator scope are merged. The ongoing scope (global) is updated with the merged scope.