-
Notifications
You must be signed in to change notification settings - Fork 7
1. Creating a Schematic
A schematic is the definition of a process (state-flow). You will define a schematic once for a certain purpose, such as how to send an email, make an API call, or more complicated flows. That schematic can then be used as the source code for instances of that flow called REstateMachines, or simply "Machines".
REstate provides a SchematicBuilder
that helps guide you through creating a schematic from scratch. Let's first take a look at what we will be building, then we will go line by line, breaking it down.
The final code will be:
var schematic = REstateHost.Agent
.CreateSchematic<string, string>("LoggerMachine")
.WithState("Created", state => state
.AsInitialState())
.WithState("Ready", state => state
.WithTransitionFrom("Created", "log")
.WithReentrance("log")
.WithAction("log info", onEntry => onEntry
.DescribedAs("Logs the transition as a message.")
.WithSetting(
key: "messageFormat",
value: "{schematicName}({machineId}) entered {state} on {input}. " +
"Message: {payload}")))
.Build();
To begin we call the create schematic method.
var schematic = REstateHost.Agent
.CreateSchematic<string, string>("LoggerMachine")
The type parameters on the method correlate to state and input types to be used in the Schematic, as seen in the definition: CreateSchematic<TState, TInput>
. The only parameter is the name of the Schematic.
Next we need to define our initial State, the State the Machine will be in when it is created.
.WithState("Created", state => state
.AsInitialState())
The State value is the string "Created"
. State values should be unique.
The second parameter is a lambda that allows you to modify the definition of the state generated. Here we use the .AsInitialState()
method.
Next we can define another State, but this time with an Action: a transition from the "Created"
state, and a special type of transition called a re-entrance, which is to say there is a transition from the state into itself, like a loop or recursion.
First, we define the State "Ready"
and start the State modifier lambda like before.
.WithState("Ready", state => state
Then, we can define a transition from our first State '"Created"' into our new State "Ready"
when the Machine receives the input "log"
.
.WithTransitionFrom("Created", "log")
Then, we can allow the re-entrant transition, "Ready" -> "Ready"
when the Machine receives the input "log"
.
.WithReentrance("log")
Now that we have the acceptable transitions defined, we can add an Action that should execute when the machine transitions into the state.
The first parameter is known as the ConnectorKey
. It is just a string
that corresponds to a Connector (and its associated configuration) that has been registered with REstate. REstate ships with a LogEntryConnector
that has several configurations available: "log info"
, "log debug"
, "log trace"
, "log warn"
, "log error"
, "log critical"
, where the second word is the logging level to use. For this example we use the "log info"
connector key since our log message provides some "business value".
The second parameter is a lambda that lets us further define the Action, just like we did previously on State.
.WithAction("log info", onEntry => onEntry
An Action can be given a description to make it less cryptic.
.DescribedAs("Logs the payload as a message.")
Going further, we can add some settings to our Connector. The settings the Connector will use vary per Connector, so refer to their documentation for what is available and how to use them, but we can take a look at the log
Connector now.
The log
Connector has just one possible setting: messageFormat
. When provided it lets you specify the format of the message that will be logged. There are a number of allowed special tokens such as {schematicName}
that will be replaced at run-time with the current Machine's values.
.WithSetting(
key: "messageFormat",
value: "{schematicName}({machineId}) entered {state} on {input}.")))
Finally to wrap it up, we can call the .Build()
method to convert the SchematicBuilder<TState, TInput>
to the serializer-friendly Schematic<TState, TInput>
.
.Build();
We will continue with the Schematic we just built and Create a StateEngine to store it.