Loan Application - Temporal.io based Worker
This tutorial in a nutshell
In this tutorial, we will build a simple Temporal-based Worker in Java language that will manage a Loan Application business process in KuFlow. Our example will model a Workflow whose purpose is to fill up an application form for approval. We will include some special rules, such as automatic approval for amounts below a certain quantity, and the use of different currencies which require REST calls to a third-party application.
Prerequisites
Before starting our Worker for the first time, we must register it in KuFlow (app.kuflow.com). After familiarizing yourself with the
user interface, you are ready to perform the necessary configurations for our Worker. To do so, click on the Settings
menu. If you do not
see this menu, you probably do not have the necessary permissions to define processes, so you will have to request it from your Organization
administrators, or simply create a FREE new account.
Create the credentials for the Worker
We will configure an APPLICATION
that will provide us with the necessary credentials so that our Worker (written in Java and located in
your own machine) can interface with KuFlow.
Go to the Settings > Applications
menu and click on Add application
. We establish the name we want and save. Next, you will get the
first data needed to configure our Worker.
- Identifier: Unique identifier of the application. For this tutorial: LoanApp
- Later in this tutorial, we will configure it in the
kuflow.api.client-id
property of our example.
- Later in this tutorial, we will configure it in the
- Token: Password for the application.
- Later in this tutorial, we will configure it in the
kuflow.api.client-secret
property of our example.
- Later in this tutorial, we will configure it in the
Finally, you get something like:
Create the process definition
We need to create the definition of the process that will execute our Workflow. In this section, we will configure the KuFlow tasks of which
it is made up as well as the information necessary to complete said tasks, the process access rules (i.e. RBAC), as well as another series
of information. To do this we go to the Setting > Processes
menu and create a new process.
A Process Definition with the following data:
- Description
- Free text description about the Workflow.
- Workflow
- Workflow Engine
- KuFlow Engine, because we are designing a Temporal-based Worker.
- Workflow Application
- LoanApp, the application to which our Worker will connect to
- Task queue
- The name of the Temporal queue where the KuFlow tasks will be set. You can choose any name, later you will set this same name in the appropriate configuration in your Worker. For this tutorial: SampleQueue.
- Type
- It must match the name of the Java interface of the Workflow. For this tutorial, SampleEngineWorkerLoanWorkflow is the name you should type in this input.
- Workflow Engine
- Metadata
- Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
- Code: REASON
- Name: Reason
- Field Type: Text
- Mandatory: Yes
- Editable: Yes (To allow editing until the process has been completed)
- Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
- Code: FIRST_NAME
- Name: First name
- Field Type: Text
- Mandatory: No
- Editable: No
- Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
- Code: LAST_NAME
- Name: Last name
- Field Type: Text
- Mandatory: No
- Editable: No
- Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
We must also define the Permissions
- At least, one user or group of users must have the role of
INITIATOR
in order to instantiate the process through the application. In this tutorial, we will allow all users from this organization to be able to process initiation (Groups > Users) - At least, one user or group of users will assume the task of loans approvals. In this tutorial, we have defined the user Murph, so we
will also give her the role of
PROCESS MANAGER
.
We define two Structure Definitions in the process with the following data:
- Structure Data Loan Application Form:
- Code: SD_LOAN_APPLICATION
- Schema:
{
"title": "Form",
"type": "object",
"properties": {
"FIRST_NAME": {
"type": "string"
},
"LAST_NAME": {
"type": "string"
},
"AMOUNT": {
"type": "number",
"minimum": 0
},
"CURRENCY": {
"type": "string",
"enum": [
"EUR",
"USD",
"GBP"
]
}
},
"required": [
"FIRST_NAME",
"LAST_NAME",
"AMOUNT",
"CURRENCY"
]
}
- Structure Data Approve Loan Form:
- Code: SD_APPROVE_LOAN
- Schema:
{
"title": "Form",
"type": "object",
"properties": {
"FIRST_NAME": {
"type": "string"
},
"LAST_NAME": {
"type": "string"
},
"AMOUNT": {
"type": "number",
"minimum": 0
},
"CURRENCY": {
"type": "string",
"enum": [
"EUR",
"USD",
"GBP"
]
}
},
"required": [
"FIRST_NAME",
"LAST_NAME",
"AMOUNT",
"CURRENCY"
]
}
We define two Tasks Definitions in the process with the following data:
- Task "Loan Application Form"
- Description: Free text description about the Task.
- Code: LOAN_APPLICATION
- Permissions:
- Groups > Users as Candidate (in this tutorial, we will allow all users from this organization to fill up a loan application form)
- Structure Data: Loan Application Form
- Task "Approve Loan"
- Description: Free text description about the Task.
- Code: APPROVE_LOAN
- Permissions:
- Murph as Candidate
- Structure Data: Approve Loan Form
We must also define the Permissions
- At least, one user or group of users must have the role of
INITIATOR
in order to instantiate the process through the application. In this tutorial, we will allow all users from this organization to be able to process initiation (Default Group) - At least, one user or group of users will assume the task of loans approvals. In this tutorial, we have defined the user Murph,
so we will also give her the role of
PROCESS MANAGER
.
Source code for our Worker
The source code for this example can be obtained from our java examples repository, and here for a version without Spring.
However, an alternative methodology could be to download the example template code for the process we are going to implement and compare it with the complete example in our repository. To download the initial template you must access the workflow definition in the App, and once configured, use the download template option.
Main technologies used in the example
This code will serve as a starting point for implementing our Loan Application worker. The requirements for its use are the following:
- Java JDK
- Maven
- To build the example. It is distributed in an integrated way so it is not necessary to install it in isolation.
- Spring Boot and Spring Cloud
- To wire all our integrations.
- Note: There is a version of this example that does not use spring available in our repository.
- IDE
- An IDE with good Java support is necessary to work comfortably. You can use VS Code, IntelliJ Idea, Eclipse or any other with corresponding Java plugins.
To make things simpler, the following dependencies have been used in our example:
- KuFlow Java SDK
- Provide some activities and utilities to work with KuFlow.
- Temporal Java SDK
- To perform GRPC communications with the KuFlow temporal service.
Implementation details
The workflow
In this section, we will make the fundamental steps to creating a workflow for this business process:
- Users in an organization can create a Loan Application.
- If the loan is over 5,000 euros, manager approval is required.
- The user can indicate the amount requested in several currencies.
- A third party API will proceed to convert the currency to Euros at the current time.
- The manager is the only authorized user to approve loans.
- The user is informed of the final decision on the loan.
We modify the method that creates the Loan Form Application task so that it returns the task to the workflow upon completion.
SampleEngineWorkerLoanWorkflowImpl.java
private ProcessItem createProcessItemLoanApplication(UUID processId) {
UUID processItemId = KuFlowWorkflow.generateUUIDv7();
ProcessItemTaskCreateParams createTaskRequest = new ProcessItemTaskCreateParams();
createTaskRequest.setTaskDefinitionCode(TASK_CODE_LOAN_APPLICATION_FORM);
ProcessItemCreateRequest createRequest = new ProcessItemCreateRequest();
createRequest.setId(processItemId);
createRequest.setType(ProcessItemType.TASK);
createRequest.setProcessId(processId);
createRequest.setTask(createTaskRequest);
this.createProcessItemAndWaitCompleted(createRequest);
ProcessItemRetrieveRequest retrieveRequest = new ProcessItemRetrieveRequest();
retrieveRequest.setProcessItemId(processItemId);
ProcessItemRetrieveResponse retrieveResponse = this.kuFlowActivities.retrieveProcessItem(retrieveRequest);
return retrieveResponse.getProcessItem();
}
private void createProcessItemAndWaitCompleted(ProcessItemCreateRequest request) {
this.kuFlowActivities.createProcessItem(request);
// Wait for completion
Workflow.await(() -> this.kuFlowCompletedTaskIds.contains(request.getId()));
}
We modify the function that manages the Loan Approval task, so that in its creation the First Name, Last Name fields are completed with the data of the initial task of the form. In the same way, the value for the Amount requested is added, once transformed to Euros from the source currency.
SampleEngineWorkerLoanWorkflowImpl.java
private ProcessItem createProcessItemApproveLoan(ProcessItem processItemLoanApplication, BigDecimal amountEUR) {
String firstName = processItemLoanApplication.getTask().getData().getValue().get("FIRST_NAME").toString();
String lastName = processItemLoanApplication.getTask().getData().getValue().get("LAST_NAME").toString();
UUID processItemId = KuFlowWorkflow.generateUUIDv7();
JsonValue createTaskData = new JsonValue();
createTaskData.setValue(Map.of("FIRST_NAME", firstName, "LAST_NAME", lastName, "AMOUNT", amountEUR.toPlainString()));
ProcessItemTaskCreateParams createTaskRequest = new ProcessItemTaskCreateParams();
createTaskRequest.setTaskDefinitionCode(TASK_CODE_APPROVE_LOAN);
createTaskRequest.setData(createTaskData);
ProcessItemCreateRequest createRequest = new ProcessItemCreateRequest();
createRequest.setId(processItemId);
createRequest.setType(ProcessItemType.TASK);
createRequest.setProcessId(processItemLoanApplication.getProcessId());
createRequest.setTask(createTaskRequest);
this.createProcessItemAndWaitCompleted(createRequest);
ProcessItemRetrieveRequest retrieveRequest = new ProcessItemRetrieveRequest();
retrieveRequest.setProcessItemId(processItemId);
ProcessItemRetrieveResponse retrieveResponse = this.kuFlowActivities.retrieveProcessItem(retrieveRequest);
return retrieveResponse.getProcessItem();
}
The notification tasks (approval or rejection) will be left as they are in the downloaded template.
We need to modify the main method of the workflow with the following content:
SampleEngineWorkerLoanWorkflowImpl.java
@Override
public WorkflowResponse runWorkflow(WorkflowRequest workflowRequest) {
LOGGER.info("Started loan process {}", workflowRequest.getProcessId());
ProcessItem processItemLoanApplication = this.createProcessItemLoanApplication(workflowRequest.getProcessId());
this.updateProcessMetadata(processItemLoanApplication);
String currency = processItemLoanApplication.getTask().getData().getValue().get("CURRENCY").toString();
String amount = processItemLoanApplication.getTask().getData().getValue().get("AMOUNT").toString();
// Convert to euros
BigDecimal amountEUR = this.convertToEuros(currency, amount);
boolean loanAuthorized = true;
if (amountEUR.compareTo(BigDecimal.valueOf(5_000)) > 0) {
ProcessItem processItemApproveLoan = this.createProcessItemApproveLoan(processItemLoanApplication, amountEUR);
String approval = processItemApproveLoan.getTask().getData().getValue().get("APPROVAL").toString();
loanAuthorized = "YES".equals(approval);
}
Process process = this.retrieveProcess(workflowRequest);
if (loanAuthorized) {
this.createProcessItemNotificationOfLoanGranted(workflowRequest, process);
} else {
this.createProcessItemNotificationOfLoanGrantedRejection(workflowRequest, process);
}
LOGGER.info("Finished loan process {}", workflowRequest.getProcessId());
return this.completeWorkflow(workflowRequest);
}
The activities
Our workflow interacts with two external systems, the first one is the KuFlow REST API. For this we use a set of activities already provided in the SDK. On the other hand, we need to connect to a third party API that provides real-time currency conversion. For this we create the following activity.
CurrencyConversionActivitiesImpl.java
@Override
public String convert(String amountText, String from, String to) {
BigDecimal amount = new BigDecimal(amountText);
String fromTransformed = this.transformCurrencyCode(from);
String toTransformed = this.transformCurrencyCode(to);
String endpoint = String.format(
"https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/%s/%s.json",
fromTransformed,
toTransformed
);
try (Scanner scanner = new Scanner(new URL(endpoint).openStream(), StandardCharsets.UTF_8).useDelimiter("\\A")) {
String json = scanner.next();
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
Gson gson = new Gson();
Map<String, Object> response = gson.fromJson(json, mapType);
Double conversion = (Double) response.get(toTransformed);
return amount.multiply(BigDecimal.valueOf(conversion)).toPlainString();
} catch (Exception e) {
throw ApplicationFailure.newNonRetryableFailure(e.getMessage(), "CurrencyConversionActivities.error");
}
}
The worker
We need to indicate that our Temporal worker, in addition to implementing the KuFlow activities, implements the activity that connects to a third-party API and obtains a currency transformation.
SampleEngineWorkerLoan.java
SampleEngineWorkerLoanProperties properties = loadConfiguration();
KuFlowApiProperties apiProperties = properties.getKuflow().getApi();
KuFlowRestClient kuFlowRestClient = new KuFlowRestClientBuilder()
.clientId(apiProperties.getClientId())
.clientSecret(apiProperties.getClientSecret())
.endpoint(apiProperties.getEndpoint())
.allowInsecureConnection(apiProperties.getEndpoint() != null && apiProperties.getEndpoint().startsWith("http://"))
.buildClient();
KuFlowTemporalConnection kuFlowTemporalConnection = KuFlowTemporalConnection
.instance(kuFlowRestClient)
.configureWorkflowServiceStubs(builder -> builder.setTarget(properties.getTemporal().getTarget()))
.configureWorker(builder -> {
KuFlowActivities kuFlowActivities = new KuFlowActivitiesImpl(kuFlowRestClient);
CurrencyConversionActivities conversionActivities = new CurrencyConversionActivitiesImpl();
builder
.withTaskQueue(properties.getTemporal().getKuflowQueue())
.withWorkflowImplementationTypes(SampleEngineWorkerLoanWorkflowImpl.class)
.withActivitiesImplementations(kuFlowActivities)
.withActivitiesImplementations(conversionActivities);
});
kuFlowTemporalConnection.start();
Worker configuration
The last step of this section is filling up the application configuration information. You must complete all the settings and replace the example values. The appropriate values can be obtained from the KuFlow application. Check out the Create the credentials for the Worker section of this tutorial.
# ===================================================================
# PLEASE COMPLETE ALL CONFIGURATIONS BEFORE STARTING THE WORKER
# ===================================================================
kuflow:
api:
# ID of the APPLICATION configured in KUFLOW.
# Get it in "Application details" in the Kuflow APP.
client-id: "15a113fa-4e95-46f1-ae83-97f73be98a22"
# TOKEN of the APPLICATION configured in KUFLOW.
# Get it in "Application details" in the Kuflow APP.
client-secret: FILL_ME
temporal:
# Temporal Queue. Configure it in the "Process definition" in the KUFLOW APP.
kuflow-queue: "SampleQueue"
Please note that this is a YAML
, respect the indentation.
We can test this scenario, initiating the process several times, in order to create different applications and approvals
Summary
In this tutorial, we have covered the basics of creating a workflow in KuFlow for loan applications. We have defined a new process definition and we have built a workflow that contemplates the required business rules defined above.
We sincerely hope that this step-by-step guide will help you to understand better how KuFlow can help your business to have better and more solid business processes.