Data Objects
Yantrix implies that business logic is deterministic and is built upon a single anemic Data Model
. Framework control objects, meanwhile, operate on small atomic "data blocks", interpreted as Record
-like type (i.e. Object
in JS, Dictionary
in Python, and so on). The Most important "data blocks" are
Context
: current condition of aFSM
, including it's control state and various meta fieldsPayload
: a useful data transferred to aFSM
with each dispatchedAction
, including its id and various meta fieldsEvent Meta
: the same asPayload
but forEvents
— messages dispatched throughEvent Bus
betweenFSMs
Each of those is represented as a keyed object (dictionary, record, etc.) with each key being processed as a separate var. In most cases, variables are immutable, and their names are basically labels for the results of Expressions calculation assignment.
Declaring States
Each node of MermaidJS State diagram creates a State and must hava an unique name. It's allowed to use note aliases, bit they are ignored by Yantrix, and States
are named with node's literal name.
Therefore, this diagram will create a FSM
with two States
A and B, regardless of node B title.
State Naming Rules and Conventions:
- Name must start with a letter.
- Name must contain only letters, digits or underscore characters.
- Name must not be longer than 255 characters.
- Name of the state should unambigiously convey the current status of the machine, to avoid confusion.
- Names should be kept as clear and concise as possible.
- Name of the state is best represented by a noun or an adjective.
Declaring Actions
In fact, an Action
is created automatically with every transition between States
. If not specified, it's given a descriptive name based on its starting and ending State
. However, it's wiser to specify names for transitions. They will then be exported as ActionDictionary
, allowing for better typing and Integrations. Actions
can be dispatched directly into FSM
API, or be generated automatically by the attached Event Adapter
, that translates an Event
from Event Bus
to an Action
with Payload
.
This diagram is exported as FSM
with 4 Actions
:
- AtoB
- BtoC
- Loop
- Reset
Creating Context
To access the Context
of a given State
, one should create a note
for this State
and use Reducers
.
Whenever you list a value on the left side of a Reducer
, it is added to a Context
of an attached State
:
Every State
has its own shape of Context
, but if they are explicitly listed and have identical names – they are copied by default. Members that are not mentioned are not copied
Default Context
If you define a member on a default node ([*]
), it will be copied through every transition and present for every State
. The default node also usually leads to a default State
with a reset-like Action
, that can be fired from every other State
. The following diagram behaves identical to the previous one.
Tip: It's a good practice to assign default values to Context
members, and even more important to do so for the default Context
Creating Payload
Payload
is a set of properties that is attached to an Action
and has, like Context
, a unique shape for every possible Action
value. Since Action
name is a primary key, all Actions
defined in a diagram must provide identical Payload
signature, and this signature is enclosed in brackets (()
)
Here, whenever a START Action is dispatched into a FSM
, Payload
must contain a counter
property, which always brings the FSM to INIT State
and sets Context
property with the same name. INIT State
itself is in ByPass
(see below), so FSM
will transition through it synchronously. That means, it is never in that State
, so receiving a START Action
will trigger the next transition with that Action
attached.
Forks
In the last diagram there is a Fork
following WORKING State
. Dispatching a REDUCE Action
at that state will transition to one of two States
depending on the Predicate
execution result. If isGreater(#value,#counter)
resolves to a truthy Binary, this Action
will transition the FSM
into END State
. If not, the same Payload
will be invoked upon WORKING State
again.
Fork
is a special node that allows to transition to different states, depending on whether a certain condition(or a set of conditions) is met. Condition that checks if it's possible to traverse a certain path is represented by a Predicate
that resolves to a Binary value.
Any Fork
can have multiple branching paths attached to it, and one default path that will be accepted if all other conditions aren't met.
Notice: There can be only one default path for any given Fork
, which means that only one path can be created without an attached Predicate
.
Conditions for every fork are processed in order of their creation in the diagram syntax, going from top to bottom. Default path for every fork is processed after processing all other conditions for this fork, regardless of its location in the diagram syntax.
A Fork
can even lead to another Fork
node, as illustrated by the diagram below:
If no condition on any branching path of the Fork
is met, and the default path is not specified - the FSM
will not transition into the next State
, until the dispatching of the next Action
.
State Flags
Flags are special modifiers that alter the behavior of the State
they a attached to.
+Init
A created FSM
will have this State
at initialization. A diagram can't have more than 1 State
with +Init
flag
+ByPass
A bypassed State
automatically propagates the acquired Action
to the next State
, if such connection exists in the diagram. The attached Reducers
are run beforehand, and then the reducers of the outbound State
are run next.
WARNING: Payload
is not propagated through ByPass
nodes and must be consumed in them.
This is the same as emitting an Event
and subscribing to it at the same time, except for this is shorter and must be used whenever a control State
must be switched through without any information getting out of the FSM
. Also note, that a +ByPass
State
can have any number of incoming transitions, but only one outgoing, which is not an Action
, but is rather coded with [-]
label: