Skip to content
Tuomo Ala-Vannesluoma edited this page Aug 21, 2023 · 27 revisions

Table of Contents

1 Minute Guide

For a super quick 1 minute guide, checkout the frontpage of nFlow repository.

Components

nFlow consist of the following main components:

  1. nflow-engine contains a multithreaded workflow dispatcher, Java API for managing workflows and the persistance layer implementation.
  2. nflow-rest-api contains a JAX-RS compliant REST service implementation for exposing workflow management and query APIs.
  3. nflow-metrics provides integration with the Metrics library.
  4. nflow-jetty contains an embeddable Jetty server for running nFlow with your custom workflows.

In addition, nflow-tests component contains integration tests over demo workflows.

Usage Scenarios

The following example scenarios illustrate how you can use nFlow with your applications.

Scenario 1: Embedded Engine Only

Use embedded nflow-engine to run your own workflows inside your own application.

Scenario 1 picture

Scenario 2: Inside Your Application Server

Same as the previous scenario except you've exposed nFlow services through REST services implemented in nflow-rest-api. You can use any REST stack (e.g. Jersey, Apache CXF, etc) that supports JAX-RS specification.

Scenario 2 picture

Scenario 3: Full nFlow Stack

Full blown nFlow running on embedded Jetty (nflow-jetty -module).

Scenario 3 picture

Anatomy of a Workflow

In nFlow terminology, you have workflow definitions and instances. A workflow definition is Java class that contains the implementation of a business process (e.g. credit application process). A workflow instance is a runtime instance of the business process (e.g. credit application from a certain customer). As a developer, you need to implement the workflow definition after which the workflow instances can be submitted through nflow-engine API or nflow-rest-api services.

A workflow can be composed of human tasks (e.g. accept application), technical tasks (e.g. call REST service) or both of these tasks. A simple workflow that involves creating a credit application, the credit decision, possible money transfer and finally closing the credit application is illustrated below. The Java code for CreditApplicationWorkflow can be found from nflow-tests -module.

Implementation Class and States Declarations

CreditApplicationWorkflow begins by extending WorkflowDefinition, which is the base class of all workflow definitions in nFlow. The state space of the workflow is configured with several ways:

  • Defining public static fields implementing WorkflowState. For example one could use io.nflow.engine.workflow.curated.State or create own implementation
  • Giving initialState or errorState to WorkflowDefinition constructor
  • Use states in permit methods will register them also
  • Pass the states to WorkflowDefinition constructor via states parameter
  • Register the states via registerState method

In this example, the states are also given a type and documentation. The following state types are supported (WorkflowStateType-enumeration):

  • start: Initial state of a workflow that is executed and retried automatically by nFlow. The workflow processing continues to the next state based on business logic implemented in the state method.
  • normal: A state of a workflow that is executed and retried automatically by nFlow. The workflow processing continues to the next state based on business logic implemented in the state method.
  • wait: A state for waiting something outside this instance to happen, including (but not limited to) child workflow instances to go to end state.
  • manual: A state that requires external action, usually a human task. nFlow executes the state method (if it exists) and then waits for the state to be update externally.
  • end: A final state of a workflow, failed or successful. nFlow executes the state method (if it exists) and then stops processing the workflow.

The state types are also used for visualizing the state diagram of a workflow.

public class CreditApplicationWorkflow extends WorkflowDefinition {
...
  private static final WorkflowState CREATE_CREDIT_APPLICATION = new State("createCreditApplication", WorkflowStateType.start,
      "Credit application is persisted to database");
  public static final WorkflowState PREVIEW_CREDIT_APPLICATION = new State("previewCreditApplication", start,
      "Check if credit application would be accepted (ie. simulate)");
  private static final WorkflowState ACCEPT_CREDIT_APPLICATION = new State("acceptCreditApplication", manual,
      "Manual credit decision is made");
  private static final WorkflowState GRANT_LOAN = new State("grantLoan", "Loan is created to loan system");
  private static final WorkflowState FINISH_CREDIT_APPLICATION = new State("finishCreditApplication",
      "Credit application status is set");
  private static final WorkflowState DONE = new State("done", end, "Credit application process finished");
  private static final WorkflowState ERROR = new State("error", manual, "Manual processing of failed applications");
...

Settings and State Transitions

Each workflow implementation must have the following properties set through base class constructor:

  • name: defines the name that is used when submitting new instances (creditApplicationProcess)
  • default start state: state from which new instances start by default (createCreditApplication)
  • generic error state: error state for generic failures (error)

Optionally you can also override default timing related settings through custom subclass of WorkflowSettings (CreditApplicationWorkflowSettings). Next you can define allowed state transitions through permit() which checks that the corresponding state handler methods exist.

public CreditApplicationWorkflow() {
  super("creditApplicationProcess", createCreditApplication, error, 
      new CreditApplicationWorkflowSettings());
  permit(CREATE_CREDIT_APPLICATION, ACCEPT_CREDIT_APPLICATION);
  permit(ACCEPT_CREDIT_APPLICATION, GRANT_LOAN);
  permit(ACCEPT_CREDIT_APPLICATION, FINISH_CREDIT_APPLICATION);
  permit(FINISH_CREDIT_APPLICATION, DONE);
}

State Handler Methods

For each start and normal state there must exist a state handler method with the same name. For manual and end states the state handler method is optional. The state handler method must be a public method that returns NextAction (start and normal states) or void (manual and end states) and takes StateExecution as an argument. NextAction class contains static methods for creating return values that control nFlow execution, like NextAction.moveToState(...), NextAction.retryAfter(...) etc. StateExecution contains the main interface through which workflow implementation can interact with nFlow (see next section). StateExecution can be followed by optional state variable definitions (see state variables).

Each start and normal state handler method must define and schedule the next state execution by returning a valid NextAction value. For instance, CreditApplicationWorkflow.createCreditApplication() defines that the next state, acceptCreditApplication, is executed immediately. Manual and final state handler methods (e.g. acceptCreditApplication and error) are optional. These methods do not need to return any value because nFlow will stop processing the workflow after the state method is executed.

public NextAction createCreditApplication(StateExecution execution, 
        @StateVar(instantiateNull=true, value=VAR_KEY) WorkflowInfo info) {
  ...
  info.applicationId = "abc";
  return NextAction.moveToState(ACCEPT_CREDIT_APPLICATION, "Credit application created");
}

public void acceptCreditApplication(StateExecution execution, 
        @StateVar(value=VAR_KEY) WorkflowInfo info) {
  ...
}

public NextAction grantLoan(StateExecution execution, @StateVar(value=VAR_KEY) WorkflowInfo info)
public NextAction finishCreditApplication(StateExecution execution, @StateVar(value=VAR_KEY) WorkflowInfo info)
public void done(StateExecution execution, @StateVar(value=VAR_KEY) WorkflowInfo info)
public void error(StateExecution execution, @StateVar(value=VAR_KEY) WorkflowInfo info)

Interacting with nFlow

The mechanisms described in this section should be sufficient to implement the interaction between your workflows and nFlow.

NextAction -class

Each start and normal state handler method must return a valid NextAction value that controls the execution of the workflow instance. Returning null will put the workflow instance in error state. To create a valid NextAction return value, use one of the following static methods:

  • retryAfter(DateTime activation, String reason): Execute the current state handler method after given activation time.
  • moveToStateAfter(WorkflowState nextState, DateTime activation, String reason): Execute the nextState handler method after given activation time.
  • moveToState(WorkflowState nextState, String reason): Execute the nextState handler method immediately.
  • stopInState(WorkflowState finalState, String reason): Move to finalState but do not execute the finalState handler method.

StateExecution -interface

StateExecution is the access point for all the workflow instance-specific information in state handler methods.

  • businessKey: optional business identifier (e.g. application specific id) for the workflow instance
  • variables: business process variables

State Variables

State variables are persistent objects/values that are workflow instance specific. State variables are stored after state handler method execution and are available in subsequent states of the process.

Optionally you can define @StateVar-annotated POJOs (must have zero argument constructor) or Java primitive types as additional arguments after StateExecution argument. The additional arguments are automatically persisted by nFlow after state execution. In CreditApplicationWorkflow class WorkflowInfo is instantiated automatically (instantiateNull=true) before createCreditApplication-method is entered.

WorkflowSettings -interface

WorkflowSettings can be accessed through WorkflowDefinition.getSettings()-method. Currently it contains timing and retry related parameters.

Setting Up Your nFlow

Using Spring Framework

Spring is the preferred way of integrating nFlow with your own application. You need to import/declare a Spring configuration bean in your Spring application context. The configuration bean type depends on the usage scenario (see section Usage Scenarios) that you selected.

nFlow will autodetect your WorkflowDefinitions that are defined as Spring beans in the same Spring application context.

Without Spring Framework

If you don't want to learn Spring, you can only use Full nFlow Stack-scenario.

Define a start class for nFlow like in 1 Minute Guide. Then add the fully qualified class names of your WorkflowDefinitions in a text file. Package the text file with nFlow and define the name of the text in nFlow property called nflow.non_spring_workflows_filename.

See nflow-tests-module for an example.