Dynamic Form Processing in Kogito
Tags: kogito, java, quarkus
Published: 2023-11-30 05:32:16

The purpose of this post is to share the journey faced by the development team at GADA Technology during the construction of a part of  our system that we will always view as an amazing achievement. We see this experience as an opportunity to grow the collective knowledge of the Kogito community and feel a sense of duty to share the unusual advancements and strange tricks we found.


### What were we able to achieve?
The goal of this exercise was to build a generic form processor for workflows. 

This would allow developers to easily build a logical flow of forms in an executable graphical representation. The idea was that this processor should be capable of handling incoming data from a frontend web application, and then pushing necessary updates back to said frontend.

---

### Versions

- JDK - 17
- Quarkus - 2.16
- Kogito - 1.36

---

### What is Kogito?
Kogito is a cloud-native business automation technology for building business applications using a graphical interface.

For additional information and complete documentation, it is advised to read the [Kogito Documentation](https://docs.kogito.kie.org/latest/html_single/).

---

### The Core Processing Workflow
Many sections of our platform can be broken down into a series of forms with varying fields required to capture data from the user. Each of these forms is designated a unique code and can be invoked via this code using a custom workflow subprocess from any of our workflows. These forms have each been assigned a set number of fields to display that the system may expect to be answered.

Invoking a form will open a process instance that we consider to be a "Task" that the user should now complete.

When a user answers any of the fields, the answer is processed by the workflow to determine validity, and store the update until ready to persist the changes. Once all mandatory fields have been answered, the form is deemed “Ready to Submit” and a submit event may be triggered by the user. A submit event will cause the answers to persist and will complete the task.

<br />

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2d6pat23lrfddwz6dr0f.png"
  alt="Core Form Processing Workflow">
  <figcaption>Core Form Processing Workflow</figcaption>
</figure>

<br />

#### Dispatch
On initialisation of the task, or when an update is required following an answer, the workflow will dispatch the latest data to update the UI.

The dispatch process consists of a large number of lines of code obfuscated behind a service task, however, the overall idea remains simple: dispatch an update to the frontend.

<br />

#### Answer
Incoming Data that is used to answer a field in the form is processed to determine validity of the data. For example, an email field must contain specific characters to be deemed valid. Once confirmed, any necessary updates are then pushed back to the frontend.

<br />

#### Submit Event
A Submit Event will trigger completion of the task much like a submit button would act with any standard form.

First, all data must be checked to ensure that the minimum requirements have been met. Once confirmed, the answers are persisted to the database for permanent storage, and the task is set to a status of "COMPLETED".

<br />

#### Reset Event
A Reset Event is used to clear all un-persisted answers. This is used when a user wishes to start fresh again on the current form. Any temporary answers are cleared, and these updates are pushed to the frontend.

<br />

#### Cancel Event
A Cancel Event is used to close and exit the current task without persisting the data entered this far.

<br />

#### Previous Event
A Previous Event is used to trigger movement between the current form and the previous form completed by the user.

<br />

#### Custom Event
A Custom Event handler is available to use in case developers want to create an adhoc event pathway. This acts much the same as the Previous Event, but should be used as an alternative if the Previous Event pathway is already being used.

---

### Invoking the Core Processing Workflow
It is important to note that for specific reasons that I will not delve into here, the workflow containing the Form Logic was required to be executed in an entirely different micro-service to that of the core processor workflow.

The following image provides an example of this all put into practice. The Sub-Process nodes (denoted by a black outline) are used here to invoke a task for the user to complete a given form.

A gateway following a task node is optional, but must be used if more complex navigational logic is required. In this example, we are providing the ability to navigate backwards and forwards between the two forms.

<br />

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qb7l9p6urpi26mfuuwb.png"
  alt="Form Navigation Via Workflow">
  <figcaption>Form Navigation Via Workflow</figcaption>
</figure>

<br />

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/76tg248myupgr203f8q6.png"
  alt="Calling the Sub-Process">
  <figcaption>Calling the Sub-Process</figcaption>
</figure>

<br />

Without greater context, the values supplied to the inputs will likely seem meaningless. It is only important to take note of the output TaskExchange object, as this is what conveys the type of event that was used to complete or exit the task, eg, SUBMIT, CANCEL, or another custom event.

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vua89dujnkp9744l33jw.png"
  alt="Sub-Process Inputs and Outputs">
  <figcaption>Sub-Process Inputs and Outputs</figcaption>
</figure>

<br />

To call the core form processing workflow from an external service, we were able to utilise the message events. This ensures a pause in the process until a response is received which signals the end of the task.

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvjpih7tr1yyq053sqe4.png"
  alt="Calling the core processor using Message Events">
  <figcaption>Calling the core processor using Message Events</figcaption>
</figure>

<br />

To receive and handle these message events, a corresponding workflow is required in the same location as the Core Processing Workflow.

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncjy9ju5vu4jw98zcnjn.png"
  alt="Receiving calls to the core processor">
  <figcaption>Receiving calls to the core processor</figcaption>
</figure>

---

### Noteworthy Findings
Listed here are some interesting findings throughout contributing to the current state of the processor.

<br />

#### Wait Indefinitely
This is used to ensure the process remains open and can be accessed by other incoming requests.

<br />

<figure>
  <img
src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/70sf83rdgibfcnf3xbff.png"
  alt="Wait Indefinitely Signal Event">
  <figcaption>Wait Indefinitely Signal Event</figcaption>
</figure>

<br />

#### Custom Process Status
Whilst the data-index GraphQL service can be queried to find information regarding the active Kogito processes, we found difficulty in accurately determining the state of a given process. As a workaround, each of our task processes were internally assigned a state to be managed by our workflow. This can be seen happening after the Cancel and Submit events.

<br />

#### Workflow Exception/Error Handling
Unfortunately, the support for many of the inbuilt existing event types is lacking in Kogito. This makes dealing with exceptions and graceful error handling rather difficult. 

Containing all errors was extremely important for any workflow processes that were to be accessed at a later point in time using an event process, which became clear when we had processes dying well into their lifecycle that were now rendered inaccessible.

<br />

#### Instability
Kogito is what we would consider a bleeding edge tool, so it was not surprising to find various points of instability. We faced various issues with data serialisation and the data-index service crashing when workflows were not always properly configured. It was generally not obvious where the root of the issue lied, and required a great deal of vigilance when defining the Process Data of each workflow.

<br />

#### Scheduling
This piece of criticism is not directly related to the examples explained in this post, however, it is still worth a mention. Kogito provides a Job Service schedular that is used via the User Tasks. Because of the constant death of workflow processes, we found ourselves having to invent a more simplistic scheduling system using Quartz.

<br />