Summary#
<Wf>.Graph is the answer to "what does this process look like?" — the question
Transitions — where this item may go next does not answer, because it is asked of ONE run and says only where that run may go next.
var g = OrderFlow.Graph;
g.States // every state, in declaration order
g.Transitions // every declared transition, each joining two real statesIt is not a server read. The graph is the declaration, the same for every run, so the compiler builds it — from
the very graph osy model, the workflow diagram and the workflow lints use — and a page reads it like a constant.
Where a run is is its own tracked field (order.Status, see Tracks and Initial (the field a workflow drives)), live like any field; where it
has been is <Wf>.For(item).TimeInStates (Flow metrics — how long an item took, and how much was waiting). Those three are everything a live picture
of a run needs.
Signature#
<Wf>.Graph // WorkflowGraph
.States // WorkflowStateInfo[] — Id, Name, Label, IsInitial, IsTerminal, Outcome
.Transitions // WorkflowTransitionInfo[] — Id, From, To, Trigger, Kind, GuardWorkflowStateInfo | |
|---|---|
Id | a key — the state's name, unique in its workflow |
Name | as declared (Placed) — what goto and the tracked enum spell |
Label | for a person: the tracked enum member's [Label], or its name |
IsInitial / IsTerminal | where a run starts; where it ends |
Outcome | Success, Cancel or Error for a terminal; empty otherwise |
WorkflowTransitionInfo | |
|---|---|
Id | a key unique in the graph |
From / To | the two states' Ids |
Trigger | what fires it: an event (Ship), complete when, enter, a clock (Expire), an SLA breach |
Kind | event, completion, timeout, cancel or sla |
Guard | a when (…) arm's condition as written; null when unconditional |
Description#
What counts as a transition#
Every way the declaration can move a run: each state's on X { goto Y; } (every arm of it), complete when (…) goto Y,
a goto in a state's enter hook, and an SLA breach body. A route declared on the workflow (on Cancel { goto Cancelled; }) applies in every state that is not terminal, so it appears once per state it leaves — each edge
joins two real states. Its Kind (cancel) is how a picture leaves that fan out when it wants to:
var moves = OrderFlow.Graph.Transitions.Where(t => t.Kind != "cancel");Reading it#
using Osysharp.Workflow;
enum ParcelStatus { Placed, [Label("Out for delivery")] Shipped, Delivered, Cancelled }
entity Parcel {
[Required, MaxLength(40)] string Ref;
ParcelStatus Status;
security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}
workflow Shipping {
Tracks = Parcel.Status;
Autostart = true;
Initial = Placed;
event Ship();
event Deliver();
event Cancel();
state Placed {
subscribe Ship();
on Ship { goto Shipped; }
}
state Shipped {
subscribe Deliver();
on Deliver { goto Delivered; }
}
on Cancel { goto Cancelled; }
terminal success Delivered { }
terminal cancel Cancelled { }
}
[Route("/process")] [AllowAnonymous]
component Process() {
render {
Stack(gap: 2) {
foreach (var s in Shipping.Graph.States) {
Text(s.Label + " — " + Shipping.Graph.Transitions.Count(t => t.To == s.Id) + " way(s) in");
}
}
}
}Drawing it#
The The flow kit — a node-and-edge canvas whose nodes are your own Osy# canvas draws a workflow from this directly — nodes: OrderFlow.Graph.States,
edges: OrderFlow.Graph.Transitions, sourceField: e => e.From, targetField: e => e.To — lays it out itself, and marks a
run's current state and path with highlighted and visited. See A live picture of a workflow run there.
See also#
- Transitions — where this item may go next — where ONE run may go next, with its guards evaluated
- Tracks and Initial (the field a workflow drives) — the field that says where a run is
- Flow metrics — how long an item took, and how much was waiting —
TimeInStates: where a run has been - The flow kit — a node-and-edge canvas whose nodes are your own Osy# — the canvas that draws it