Executing Logic
Workflows4s supports two ways of embedding custom logic within a workflow:
- Deterministic (Pure) Operations - operations that always produce the same output for the same input, without any side effects
- Nondeterministic Operations - operations that may produce different outputs for the same input, typically involving side effects
Deterministic operations are executed with each workflow recovery, while nondeterministic ones are memoized through event persistance.
Deterministic (Pure) Operations
Without Error
val doThings: WIO[Any, Nothing, MyState] =
WIO.pure(MyState(1)).autoNamed
val doThings2: WIO[MyState, Nothing, MyState] =
WIO.pure
.makeFrom[MyState]
.value(state => MyState(state.counter))
.autoNamed
Rendering Outputs
- Flowchart
- BPMN
- Model
{
"meta" : {
"name" : "Do Things",
"error" : null
},
"_type" : "Pure"
}
With Error
val doThingsWithError: WIO[Any, MyError, Nothing] =
WIO.pure.error(MyError()).autoNamed
val doThingsWithError2: WIO[MyState, MyError, Nothing] =
WIO.pure
.makeFrom[MyState]
.error(_ => MyError())
.autoNamed
Rendering Outputs
- Flowchart
- BPMN
- Model
{
"meta" : {
"name" : "Do Things With Error",
"error" : {
"name" : "My Error"
}
},
"_type" : "Pure"
}
Unnamed pure elements are considered technical in nature and are not rendered in diagrams.
To make them visible, use .named("Name")
or .autoNamed
.
Nondeterministic Operations (Side Effects)
Nondeterministic operations involve side effects like API calls, database operations, or other external interactions. These operations need special handling because they shouldn't be re-executed during workflow recovery.
Workflows4s currently supports onyl cats-effect IO. If you're interested in other effect systems, please check here.
Without Error
val doThings: WIO[MyState, Nothing, MyState] =
WIO
.runIO[MyState](state => IO(MyEvent()))
.handleEvent((state, event) => MyState(state.counter + 1))
.autoNamed()
Rendering Outputs
- Flowchart
- BPMN
- Model
{
"meta" : {
"name" : "Do Things",
"error" : null,
"description" : null
},
"_type" : "RunIO"
}
With Error
val doThingsWithError: WIO[MyState, MyError, MyState] =
WIO
.runIO[MyState](state => IO(MyEvent()))
.handleEventWithError((state, event) =>
if true then MyState(state.counter + 1).asRight
else MyError().asLeft,
)
.autoNamed()
Rendering Outputs
- Flowchart
- BPMN
- Model
{
"meta" : {
"name" : "Do Things With Error",
"error" : {
"name" : "My Error"
},
"description" : null
},
"_type" : "RunIO"
}
Errors render in flowchart only when they are handled
With Description
You can add descriptions to RunIO operations to provide additional context in diagrams and documentation:
val doThingsWithDescription: WIO[MyState, Nothing, MyState] =
WIO
.runIO[MyState](state => IO(MyEvent()))
.handleEvent((state, event) => MyState(state.counter + 1))
.autoNamed(description = "This operation increments the counter by one")
Rendering Outputs
- Flowchart
- BPMN
- Model
{
"meta" : {
"name" : "Do Things With Description",
"error" : null,
"description" : "This operation increments the counter by one"
},
"_type" : "RunIO"
}