Repository navigation
5.5.2.2. Flows
A UML Activity Flow is a directed edge between Activity nodes. In Stateflows, a flow determines when its target can run and, for object flows, which tokens are offered to it. Flows are declared on their source node with AddControlFlow(...) or AddFlow<TToken>(...).
| Kind | Carries | Typical use |
|---|---|---|
| Control flow | A control token, not application data | Enable a target action or control node |
| Object flow | Values of a declared token type | Move application data between nodes |
| Transformation flow | Input values transformed to another type | Convert data while it traverses an edge |
Object-flow guards inspect one token and decide whether that token traverses the edge. A transformation can replace it with another token type. SetWeight(n) sets the number of eligible tokens required on that flow before its target can activate.
.AddAction(
"CreateRequest",
async c => c.Output(new Request("ORD-42", 125)),
b => b.AddFlow<Request>("Validate", flow => flow
.AddGuard(async c => c.Token.Amount > 0)
.AddTransformation(async c => c.Token.OrderId))
)
.AddAction(
"Validate",
async c =>
{
foreach (var orderId in c.GetTokensOfType<string>())
{
// Continue processing the validated order identifier.
}
})Use a control flow when only execution order matters. Use an object flow when the target needs typed data. Its generic token type selects source tokens; a transformation can change the type delivered to the target.
Flows do not all mean "run the target once for every edge." Nodes activate according to their incoming-flow rules and options. By default, Action nodes use implicit join and fork behavior; explicit Join, Merge, and Fork nodes make synchronization and branching visible in the graph. See Control Flow, Data Flow, and Implicit Fork and Join.
Activity node names are globally unique, including across nested scopes.
Ordinary node names are used as supplied. Initial nodes use
{parentName}.initial; input pins use a scope-qualified generated name.
AddFinal(name) uses FinalNode.Name when its optional name
is omitted. Output pins use OutputPin<TToken>.Name by default, or a supplied
globally unique name.
AddFlow<TToken>("name") and AddControlFlow("name") resolve a globally
unique registered node name. A target outside the source's containing scope is
automatically treated as a crossing:
// Registered on a root-level producer; offers to the action named "consume".
node.AddFlow<Item>("consume");
// Registered on an action inside "work"; offers directly to a root action.
node.AddFlow<Result>("record");
node.AddControlFlow("notify");Flow callbacks work the same way for decision/else and weighted builders; configure guards and transformations normally. Unknown target names and unsupported crossing directions are rejected at definition build time. Typed input names include their containing scope. Output pins can be given distinct names when their default names would collide; use an explicit string flow target for a custom-named pin.
Ordinary StructuredActivity scopes support incoming and outgoing crossings, including nested and sibling routing. Offers to inactive destinations stay pending; every destination containing scope must first start through its normal activation path. Crossing offers never initialize a containing structure. Later offers can enter an active scope, including an idle active scope resumed by another call. Incoming crossings into reentrant structured scopes are rejected: there must be one eligible invocation per enclosing execution scope.
ParallelActivity and IterativeActivity allow outgoing crossings only, targeting nodes directly in their immediately enclosing scope. Crossing into either boundary, even when the target is a deeper descendant, is invalid. Routing across siblings must observe the sibling destination's activation.
The destination owns execution, cancellation, partition selection, and checkpoint continuation. Offered token provenance is preserved. Work accepted outside an inner scope is not canceled by that inner scope's termination. Escaped continuations do not hold the source structure open; their destination scope drains the work and observes its failures. Normal guards, transformations, weights, and implicit joins still govern activation; a crossing does not promise one firing per offered token or an arrival order between parallel partitions.
Crossings are independent of output pins: they do not enable interleaving or change structured/parallel/iterative aggregate boundary-output semantics.
- Nodes and Tokens
- Decision Node and Merge Node
- Fork Node and Join Node
Home page Support Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0. © by Mikołaj Milewski, 2026