Skip to main content

Executing Logic

Workflows4s supports two ways of embedding custom logic within a workflow:

  1. Deterministic (Pure) Operations - operations that always produce the same output for the same input, without any side effects
  2. 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

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
note

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.

info

The effect type used in runIO is determined by the Effect type in your WorkflowContext. While the method is named runIO, it works with any effect type — cats.effect.IO, Future, zio.Task, Try, etc.

Without Error

val doThings: WIO[MyState, Nothing, MyState] =
WIO
.runIO[MyState](state => IO(MyEvent()))
.handleEvent((state, event) => MyState(state.counter + 1))
.autoNamed()
Rendering Outputs

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
note

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

Drafting support

Executing logic comes with drafting support.

val basic = WIO.draft.step()

val withError = WIO.draft.step(error = "MyError")