Chapter 5. Design and Development

5.1. BPM Suite Example Application

This reference architecture includes an example application that is designed, developed, deployed, tested and described herein. The application consists of a business process that manages the various automatic and manual steps involved in processing a mortgage application, up until the approval or denial of the mortgage.

This application is alternatively referred to as BPM Suite Example Application, jboss-bpmsuite-example and mortgage demo in various contexts and is available for download both as an attachment to this reference architecture and as a download from the Red Hat Customer Portal as part of the Quick Starts. Due to divergent update schedules and restricted release cycles, the copy used in this reference architecture may at different times be either older or newer than the Quick Starts.

While a complete copy of the example application is provided with this reference architecture, this section walks the reader through every step of design and development. By following the steps outlined in this section, the reader is able to replicate the original effort and recreate every component of the application. This document explains the design decisions at each step and outlines some best practices throughout.

5.2. Project Setup

5.2.1. Business Central

This reference architecture assumes that the previous installation and configuration steps have been followed and the environment set up. The document further assumes that a user has been set up with the security role of admin. Creating the project and developing the application as outlined in this section is mutually exclusive with cloning the provided repository and importing the artifacts. If the attached repository has been cloned into the Business Central environment, remove this repository before following these steps.

To use Business Central once BPMS has started, point your browser to http://localhost:8080/business-central and log in as a user with admin privileges:

Business Central Login

image::images/business-central-login.png[Business Central Login, scaledwidth="90%", align="center

The welcome page of Business Central provides a high level overview of its various capabilities in several tabs. The top-level menu provides persistent navigation across various pages and sections.

Figure 5.1. Business Central Welcome Page

Business Central Welcome Page

5.2.2. Repositories

Business Central stores all business rules, process definition files and other assets and resources in an asset repository (knowledge store), which is backed by a Git repository. This makes it possible to import an entire knowledge store by cloning a Git repository or interact with the knowledge store through its Git URL.

To set up a new repository, navigate to Administration from the Authoring menu:

Figure 5.2. New Repository Creation

New Repository Creation

Name the new repository "Mortgage" and assign it to the "example" organizational unit.

Figure 5.3. New Repository Information

New Repository Information

After creating the Mortgage repository, the Administration view will display both the pre-existing and newly created repositories and the URL to access each through Git.

Figure 5.4. Administration Repository List

Administration Repository List

5.2.3. Projects

Once a repository is created, the next step is to create a project inside that repository. Select Project Authoring from the Authoring menu, then switch the current repository to Mortgage in the Project Explorer breadcrumb trail as seen below:

Figure 5.5. Switching Projects inside Project Authoring

Switching Projects inside Project Authoring

Open the New Item menu. In the absence of a project, the only active option is to create a new project:

Figure 5.6. Creating a New Project

Creating a New Project

Create a project called MortgageApplication with the information provided below:

Figure 5.7. Project Settings

Project Settings

5.3. Data Model

A BPMS project typically contains many different types of assets. While the dependencies of these assets can often be complicated, the data model is almost always the most basic building block.

The BPMS web designer includes a web-based custom graphical data modeler. Under New Item, select Data Object to create necessary POJO definitions. For example, this project requires a persistable class called Applicant to represent the mortgage applicant information. The identifier is the Java class name while the label is a more user-friendly name for the type. Create the required data model under the existing com.redhat.bpms.examples.mortgage package:

Figure 5.8. Applicant Object Creation

Applicant Object Creation

Once a type has been created, proceed to define its fields alongside the automatically generated id field (for persistence) by means of the +add field button:

Figure 5.9. Applicant Object Properties

Applicant Object Properties

Each field consists of an identifier and a user-friendly label. The type of each field can either be a primitive or basic data type, or a custom type that has been previously creating using the data modeler or imported into the project. For example, an applicant would have a name which is in basic string format:

Figure 5.10. Applicant Name Field Creation

Applicant Name Field Creation

Using the steps above, create additional fields to complete the Applicant data model as well as further required object types, as listed below. Upon completion of each object, be sure to Save, providing a relevant commit message for each:

Table 5.1. Applicant

IdLabelType

name

Applicant Name

String

ssn

Social Security Number

Integer

income

Annual Income

Integer

creditScore

Credit Score

Integer

Table 5.2. Property

IdLabelType

address

Property Address

String

price

Sale Price

Integer

Table 5.3. ValidationError

IdLabelType

cause

Cause of Error

String

Table 5.4. Appraisal

IdLabelType

property

Appraised Property

com.redhat.bpms.examples.mortgage.Property

date

Appraisal Date

Date

value

Appraised Value

Integer

The final data object, Application consists of several of the above-defined types, as well as a List of validation errors, which is created using the List checkbox on the field creation modal:

Table 5.5. Application

IdLabelType

applicant

Applicant

com.redhat.bpms.examples.mortgage.Applicant

property

Property

com…​mortgage.Property

appraisal

Appraisal

com…​mortgage.Appraisal

downPayment

Down Payment

Integer

amortization

Mortgage Amortization

Integer

mortgageAmount

Mortgage Amount

Integer

apr

Mortgage Interest APR

Double

validationErrors

Validation Errors

List<com…​mortgage.ValidationError>

Overall, the complete data model consists of five custom data types, each with a number of basic fields and two using custom fields of types previously defined in the same data model:

Figure 5.11. Completed Data Model

Completed Data Model

5.4. Business Process

5.4.1. Create New Process

Within the New Item menu previously used to start creation of Data Object, select Business Process to initialize a new process within the com.redhat.bpms.examples.mortgage package:

Figure 5.12. Create New Business Process

Create New Business Process

Figure 5.13. New Process Details

New Process Details

Creating the process automatically opens the web process designer and provides a blank canvas to start the process design:

Figure 5.14. New Process Canvas

New Process Canvas

Going forward, while focusing on the canvas design, you may find it helpful to rearrange some of the tooling to allow more canvas space. You can click the '<' button next to the Open Project Editor button to collapse the Project Explorer window, then note the '>>' button on the left-hand side of the canvas, which will be used to access various predefined object types:

Figure 5.15. Expanded Canvas View

Expanded Canvas View

Designing a business process is an iterative approach. The starting point is often an existing manual process that is documented and refined in multiple stages. For the purpose of this reference architecture, it is safe to assume that a detailed conceptual design is available. Even when the design is known with a great level of depth in advance, proceeding to model it in one or two quick steps is rarely a good idea.

Start by creating a very simple process that is structurally complete and can be executed and tested. For this example, place a script node (available in the toolbar under Tasks) after the start node and complete the process by connecting this script node to a None end node (available under End Events).

Create a process variable representing a mortgage application by clicking on the white canvas background and accessing the expandable properties pane on the right-hand side, seen as Variable Definitions. The Application class, created in the com.redhat.bpms.examples.mortgage package in the data modeler serves this purpose. Call this process variable application.

Figure 5.16. Accessing Process Properties

Accessing Process Properties

Figure 5.17. Process Variables

Process Variables

Before the process can be saved, make sure to review the process properties. Assign the same com.redhat.bpms.examples.mortgage package to the process itself; ideally also add the full package name as a prefix to the process ID:

Figure 5.18. Process Properties

Process Variables

Utilize the Save button in the upper toolbar to save the process and provide a meaningful explanation of the changes thus far:

Figure 5.19. Process Save/Commit

Process Save/Commit

5.4.2. Initial Process Form

Now that the process skeleton has been created and saved, a process form is required to provide values for the mortgage application and test the process. While remote callers can utilize a REST API to start a new instance of the MortgageApplication business process, users can also provide the same information through a Business Central form, which we’ll create now.

The process uses the custom Application object type, which itself embeds the other custom types created in the data modeler. The process form, therefore, should also embed two subforms called MortgageApplicant and MortgageProperty to collect information for the Application’s subtypes. The naming pattern serves to remind that these subforms are not generic forms used for the corresponding data types, but rather created to represent the applicant and property fields of the application object. For example, ApplicationApplicant does not include a field for creditScore, since creditScore is not part of the input; it is calculated based on credit data that can be obtained with the applicant’s social security number.

5.4.2.1. Applicant Subform

We’ll start the process by first creating the two subforms for Applicant and Property that will go into the Application form. From the New Item menu, select Form and provide the name MortgageApplicant:

Figure 5.20. Create Form

Create Form

Create a new form called MortgageApplicant. Add a data origin item called applicant, with Input Id and Output Id of applicant as well. These fields indicate what variable should be used to derive existing values when the form is opened and where to store the user-provided input upon submitting the form.

Select the render Type by choosing From Data Model, com.redhat.bpms.examples.mortgage.Applicant and pick a render color.

Figure 5.21. Creating the Applicant Subform

Creating the Applicant Subform

After adding the data holder, go to Add fields by origin in the form editor toolbar and expand the applicant node in the left-hand tree view. Click the white arrows next to name, ssn, and income fields to add them to the form. While hovering over the fields now present on the form, access the pencil icon to edit the properties for each input field and provide meaningful labels for each:

Figure 5.22. Applicant Subform Field Labels

Applicant Subform Field Labels

Once complete, be sure to Save and provide a commit message for the new applicant subform.

5.4.2.2. Property Subform

Follow the same steps to create a new MortgageProperty subform for the Property object, using both the address and price fields.

Figure 5.23. Property Subform

Property Subform

Following completion, be sure to save the form, then expand/use the project explorer pane on the far left-hand side to return to the MortgageApplication business process.

Figure 5.24. Using Project Explorer

Using Project Explorer

5.4.2.3. Finishing the Initial Process Form

With the needed subforms now complete, the process form itself can be finished. When a process instance is kicked off in Business Central, the expected process form name, which will be shown to the user for collecting initial input, is derived from the process ID. The easiest and least error-prone approach to creating a process form when the correct name is to open the process and select Edit Process Form from the top menu, then select Graphical Modeler:

Figure 5.25. Edit Process Form

Edit Process Form

As before, add application as a data holder, which will be the only object needed for this form:

Figure 5.26. Application Form Data Holder

Application Form Data Holder

Once again, go to add fields by origin to add the individual fields. Add applicant, property, down payment and amortization respectively:

Figure 5.27. Application Fields by Origin

Application Fields by Origin

Those fields of application that are not of basic and primitive type (applicant and property) cannot be directly mapped to a field on the form. Edit each such fields and associate them with a previously created form that corresponds to the data type.

Edit the applicant field properties via the pencil icon on hover. Set its label to the more user friendly value of Mortgage Applicant. The field type should be simple subform. Select the previously created Applicant subform as the default form for this field, which should then be reflected on the graphical editor:

Figure 5.28. Applicant Form on Application

Applicant Form on Application

Follow similar steps to use the Property subform for the property field and set proper labels for down payment and amortization, making sure to save the form when complete:

Figure 5.29. Application Form

Application Form

5.4.2.4. Testing the Initial Process Form

Back in the MortgageApplication Business Process editor, use the properties panel of the script node to select the Script property, and inside the code editor, add a println statement for the application object:

Figure 5.30. Editing the Script Node

Editing the Script Node

Back on the editor canvas, click to select the green starting node, then click/drag the black arrow over to the script node and release, which will establish the process flow between the start and the script node. Likewise, create a connection between the script node and red end node to complete a simple, testable process.

Figure 5.31. Canvas Element Properties

Canvas Element Properties

Figure 5.32. Process Flow Connected End-to-End

Process Flow Connected End-to-End

After saving the process, click the Open Project Editor button in the left-hand Project Explorer pane. In the toolbar atop the now open Project Editor, click the Build dropdown and select Build & Deploy. Once built successfully, go to Process Management in the top-most gray toolbar, then Process Definitions and find the process listed there. Click the Start button to kick off an instance of the process:

Figure 5.33. Start Process Instance

Start Process Instance

Once the process is initialized, the process Application form that was created earlier will be opened by Business Central and presented to the user to allow for initial values for the mortgage application:

Figure 5.34. Mortgage Application Form

Mortgage Application Form

The button at the bottom of this form submits the provided data and creates a new process instance. An instance of the Application Java class is automatically created and provided to the process. The script node of the process prints out the application object so in the server log (or standard output of the server process), a line similar to the following gets printed:

13:43:06,394 INFO [stdout] (http-/0.0.0.0:8080-4) Application: com.redhat.bpms.examples.mortgage.Application@665b07a0

To see the values stored in the application object, we could edit the Application class (which is merely an annotated JavaBeans class) and add a toString() method to it. However, it should be noted that any subsequent edits by the data modeler would remove any such enhancements. The easier and more permanent alternative is to use the get methods to print the contents of the submitted application within the script node.

5.4.3. Validation

5.4.3.1. Validation Process Model

It is best practice to validate any input data. In a business process, the importance of this step is increased by the ability to correct data through human interaction.

Rule engines are often a good fit for data validation, as they allow validation rules to be stated individually and be enforced in concert, while making it easy to update each rule. BPMS includes a world-class rule engine, which makes the use of rules for validation an obvious choice.

It is also best practice to maintain the process in a valid state as often as possible throughout development. New features can be added step by step, then saved, built and tested before considering the next item on the agenda.

To begin adding validation, return to Project Authoring & move the script node and end node to the far right of the canvas, break the flow from the start node and connect it with a new Data-based Exclusive (XOR for short) gateway instead:

Figure 5.35. Adding XOR Gateway Node

Adding XOR Gateway Node

Follow that up with a Business Rule task node and connect it with the XOR Gateway:

Figure 5.36. Business Rule Task Node

Business Rule Task Node

Place another XOR gateway after the business rule task. This new gateway will be used to direct the process flow into two separate directions:

  • The process will continue executing and moves forward if the data is valid.
  • The process will go through correction if data is invalid; process flow will loop back into the first gateway, where it will undergo validation again.

For the valid case, use a sequence flow to connect the gateway all the way to the script task that was previously created to print the application data.

Figure 5.37. First Steps of Validation Flow

First Steps of Validation Flow

The first gateway, which is the second node of our process, is a converging gateway. It expects two or more incoming sequence flows but regardless of how the process ends up at this step, provides a single outgoing flow to the next node. The second gateway (fourth node) in this diagram is a diverging XOR gateway, which accepts a single incoming sequence flow but has two or more outgoing flows (we’ll add the second shortly). Java conditions or business rules determine which sequence flow is taken but in an XOR gateway, one and only one outgoing flow will always be chosen.

To place the data correction flow above the main flow, select all the existing nodes by drawing a large rectangle around them in the canvas. Once all the nodes are selected, drag one of the nodes and move it down, leaving sufficient room for at least two other rows above the main sequence where the nodes are currently located. When several nodes are selected, moving one of them moves all the selected nodes at the same time.

From the diverging XOR gateway, create a second outgoing sequence flow that connects it to a new task node. The easiest way to do this is to click on the gateway and wait for the web designer shortcuts to appear and then choose the rectangular node from the shortcut pallet. Then move this new task node directly above the diverging XOR gateway. Use the tools icon to turn it into another business rule task.

From this new business rule task, create another task node and place it to its left, directly above the converging (first) gateway. Change the type of this new node to a User Task. Use the sequence flow from this new user task to connect it back to the converging node directly underneath it.

At this point, the process is almost complete but a few refinements are required before the project can be built. Additionally, for the purpose of modeling and readability, it is important to name some of these elements.

To name an element or sequence flow in the web designer, simply double-click on the item in question and type the name. At minimum, label the first business rule task as Validation, the second business rule task as Reset Validation and the user task as Data Correction. It is also helpful to label the two outgoing sequence flows from the diverging XOR gateway as Valid and Invalid.

Figure 5.38. Validation Process Flow

Validation Process Flow

The validation node links to business rules provided in the same package. Rules are designated as part of a rule flow group to be associated with a business rule task. Click on the validation node and edit its properties. Set the Ruleflow Group property to validation:

Figure 5.39. Validation Node Properties

Validation Node Properties

Similarly, set the rule flow group for the second business rule task to resetValidation.

A business rule task can be thought of as an external service with a loose contract. The skill set of the process modeler may in fact be considered distinct from the skill set of a rule analyst. For validation, the data model serves as the common ground to define the interface. Rules require a number of facts to have been inserted into the rule engine’s working memory. In this instance, instances of the following custom types must be inserted:

  • Application
  • Applicant
  • Property

Validation rules apply constraints to these objects and their fields. The assumed contract is that in the instance that a validation rule is found to be violated, a ValidationError object would be generated and inserted into the working memory.

5.4.3.2. Validation Business Rules

To validate the correctness of supplied data as part of the mortgage application, write a number of business rules using the web designer’s guided rule editor.

For example, assume that this business does not offer mortgages for any properties with a sale price that is lower than $50,000. To enforce this rule, create a new item of type guided rule and call it Validate Property Price. Click the plus sign across from when to create the condition for this rule. From the dialog that opens, select Property to declare the constraint on the price field of the property:

Figure 5.40. Adding Property to Guided Rule

Adding Property to Guided Rule

The guided rule will then include a numbered item for its condition, simply stating:

There is a Property

To further refine the condition, hover and click on There is a Property to open a dialog and add a restriction on the price field:

Figure 5.41. Adding Property Price

Adding Property Price

Use the drop-down to constrain price when less than a value; click the pencil icon and declare the value to be a literal value and enter it as 50000.

Figure 5.42. Property Price Low Constraint

Property Price Low Constraint

Now click the plus icon to the far-right side of THEN to create a consequence for this rule. From the dialog, select to insert a ValidationError when the rule’s conditions are met, which in this case means when the property price is less than 50,000. Click the text Insert ValidationError and provide a literal value for the cause of "Property price too low":

Figure 5.43. Adding ValidationError

Adding ValidationError

Figure 5.44. Literal Value for Error Cause

Literal Value for Error Cause

Next, click on show options and then click the corresponding plus icon to the right of Attributes to create a new option. Choose the ruleflow-group attribute from the drop-down and set its value to validation, thereby linking the new rule to the rule group triggered by the validation task node:

Figure 5.45. Linking Ruleflow Group

Linking Ruleflow Group

Save this rule and enter a meaningful description for the purpose of the repository revision. The ruleflow-group of this rule ties it to the business rule task node we added to the process earlier and causes the process engine to evaluate this rule when that node is reached. If the condition of the rule it true, the rule is said to have fired, which means its consequence, also known as its action, will be executed. In this case the action is to create a new instance of the ValidationError class, set the value of its cause field to a descriptive message and insert the object in the working memory. The XOR gateway in the process then looks at the working memory to decide which path to take.

An example of a slightly more complicated rule is one that validates the amount of the down payment but ensuring that it is not a negative number and also that it is not larger than the sale price of the property itself.

For this purpose, create a rule called Validate Down Payment. Add a condition and select Property as the constraint. Click on There is a Property and enter a variable name for this constraint; for example: property.

Click the plus logo across from this new constraint which also has a down arrow superimposed on it. This indicates that a new constraint will be added directly under the constraint in question. This time declare the constraint to apply to Application. Further configure the generated There is an Application constraint by clicking on it and choosing to add a Multiple field constraint of type Any of (Or). Now click on the any of the following sentence and add a restriction on its downPayment field. Constrain any down payment that is less than the literal value of 0, then click the any of the following text again to add another constraint. Select the option or greater than and use the Expression editor to select property.price. When these conditions apply, then choose to Insert fact ValidationError with its cause field set to Down payment can’t be negative or larger than property value.

Once again, click show options and add the ruleflow-group attribute of validation before saving and committing the rule to the repository.

Figure 5.46. Down Payment Validation Rule

Down Payment Validation Rule

Next, create another guided rule and call it Validate Income. Add the constraint on the Applicant. Add a restriction on the income field of the applicant and look for an income that is less than 10000. Once again, create a corresponding validation error with an appropriate cause description such as Income too low. Remember to add a ruleflow-group attribute to the rule and set it to validation.

Figure 5.47. Income Validation Rule

Income Validation Rule

Save and now create another guided rule and call it Validate SSN. This will be a simple validation to make sure that the provided social security number is nine digits long and does not start with a zero. A more comprehensive validation is possible by following the guidelines of the Social Security Administration.

Add the constraint on the Applicant. Add a restriction on the ssn field of the applicant and look for any number that is either less than 100000000 or greater than 999999999.

Create a corresponding validation error with an appropriate cause description of Invalid Social Security Number. Add a ruleflow-group attribute to the rule and set it to validation:

Figure 5.48. SSN Validation Rule

SSN Validation Rule

Finally, create the last validation rule and call it Validate Amortization. Assume that only fixed-rate mortgages of 10, 15 and 30 years are provided by this business. Any amortization value other than these three would therefore be rejected.

Add a constraint on the Application. Add a restriction on the amortization field of the application and make the rule applicable if the amortization is not contained in the (comma separated) list. Provide a literal value for the list of the acceptable amortization values: 10, 15, 30. Create a corresponding validation error with an appropriate cause description of Amortization can only be 10, 15 or 30 years. Add a ruleflow-group attribute to the rule and set it to validation.

Figure 5.49. Amortization Validation Rule

Amortization Validation Rule

5.4.3.3. Retracting Facts After Validation

Given the possibility that multiple processes may run in a single knowledge session in a single-threaded model, it is important for the rules to clean up after themselves. In this case, the last set of rules would proceed to remove the Application, Applicant and Property objects that have been inserted for the express reason of validation. A negative salience attribute is employed on the rules to ensure that they don’t execute before the actual validation rules.

Create a New Item of type DRL file and call it Retract Facts After Validation. Write rules that simply seek and remove any facts of these known types:

package com.redhat.bpms.examples.mortgage;

rule "Retract Applicant after Validation"
    dialect "mvel"
    ruleflow-group "validation"
    salience -10
    when
        fact : Applicant( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end

rule "Retract Application after Validation"
    dialect "mvel"
    ruleflow-group "validation"
    salience -10
    when
        fact : Application( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end

rule "Retract Appraisal after Validation"
    dialect "mvel"
    ruleflow-group "validation"
    salience -10
    when
        fact : Appraisal( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end
rule "Retract Property after Validation"
    dialect "mvel"
    ruleflow-group "validation"
    salience -10
    when
        fact : Property( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end

5.4.3.4. Resetting Validation

Once a validation error has been raised, the process enters a loop of data correction and validation, until such time that the data is deemed completely valid. Errors are signaled by inserting a ValidationError object in the rule engine’s working memory. This object is used by the XOR gateway to determine if data correction is necessary, but immediately after such a determination, the error object must be removed so that the next validation can take place with a clean slate.

To reset validation, write a simple guided rule that looks for the ValidationError object and removes it. Associate the rule with the corresponding business rule task in the process by specifying the correct ruleflow group.

Figure 5.50. Reset Validation Rule

Reset Validation Rule

5.4.3.5. Linking Validation Rules & Process

Returning to the validation process flow in the process editor, click on the Invalid sequence flow and set its conditions. Change the condition expression language to drools and the expression itself to ValidationError() so that this sequence flow is taken when the supplied rules of group validation have instantiated an instance of ValidationError.

Conversely, set the expression for the Valid sequence flow to not ValidationError() while also choosing drools as the language.

Figure 5.51. Setting Valid/Invalid Conditions

Setting Valid/Invalid Conditions

Next, select the Validation task node and open the dialog for On Entry Actions. These actions are lines of Java code that execute before the node itself. Insert Application, Applicant and Property into the rule engine working memory:

Figure 5.52. Validation On-Entry Actions

Validation On-Entry Actions

Notice that the application also contains a field to represent the mortgage amount. This is a derived value, based on the property sale price and the down payment. Setting this value on entry of the validation task is not ideal but there are a number of constraints and each other option has its own disadvantages:

  1. getMortgageAmount() can be written as a utility method that does the subtraction upon request. The big disadvantage of this approach is that it is not compatible with the data modeler and even if the class is manually modified to add such a method, a future update to the data type through the data modeler may overwrite it.
  2. A rule can be written to calculate the mortgage amount and update the application object with it. Such a simple rule does not merit its own business rule task in the process and including it in validation rules is a poor choice. That implies that this value is only needed for validation, which is not the case.
  3. Creating a separate script task (or for that matter, an earlier business rule task) to calculate the mortgage amount exposes this step as part of the model. The business process model should remain high level and exclude trivial and technical steps.
  4. Data correction may indirectly result in a change to the mortgage amount so the subtraction must occur within the validation loop and the amount cannot be calculated earlier, at the time of initial data collection or its processing.

5.4.4. Data Correction

5.4.4.1. Data Correction Process Model

Data correction in the process is performed by a mortgage broker through the human interaction features of BPMS. For this purpose, a user task is created and assigned to the broker group. By assigning a task to a group, as opposed to a user, a certain degree of loose coupling between the work and the worker is achieved. Any broker who is available can claim or be assigned the created task and through the use of swimlanes, it can be mandated that the same specific user work on future tasks for this process instance, so that a desired degree of continuity is provided to the customer.

Edit the properties of the Data Correction task node. Set the Task Name to DataCorrection. This attribute is the technical name of the task, as opposed to its display name, which has already been entered into the model.

Set the Groups attribute to broker so that the task may be assigned to any broker.

Figure 5.53. Data Correction Properties

Data Correction Properties

Select the top-right icon for Edit I/O over the Data Correction node. Click Add next to Data Inputs and Assignments, which will serve to provide the application data to the human actor reviewing and completing the human task. Provide the entire mortgage application data to the broker by declaring the input variable with a distinct name of taskInputApplication and type of com.redhat.bpms.examples.mortgage.Application.

The broker will review the data provided as part of the mortgage application and make any necessary corrections so that it would pass validation next time. To correct the data, the broker may need to contact the applicant or other intermediaries. Human tasks allow automated processes incorporate such manual steps.

Similar to the input variable, create an output variable for the data correction task to receive the corrected mortgage application. Declare the output variable with a distinct name of taskOutputApplication and type of com.redhat.bpms.examples.mortgage.Application.

Figure 5.54. Data Correction Input/Output

Data Correction Input/Output

By declaring the above input/output, we have mapped the process variable application to the task variables:

  1. application variable is mapped to taskInputApplication so that the broker can view the data and proceed to correct it.
  2. taskOutputApplication back to the application variable of the process so that any corrections made by the broker are applied to the entire process going forward.

5.4.4.2. Data Correction Form

When Business Central is used to work on human tasks, a task form is typically required for every user task node created in a process. Click on a task node, then the Edit Task Form icon just above the node to create or edit a corresponding task form. As before, select the Graphical Modeler to build the form.

The data correction task form is very similar to the process form. The biggest difference is that the application variable is named differently for the task and there are, in fact, separate variable names for application on its input to and output from the task:

Figure 5.55. Data Correction Data Holders

Data Correction Data Holders

Similar to the process form, go to add fields by origin to add the individual fields. Add applicant, property, down payment and amortization respectively.

Edit each such field and set a proper label for them.

In the case of applicant and property, in additional to setting a user friendly label, also set the corresponding previously created form as the default form of the field. The final data correction form looks as follows:

Figure 5.56. Data Correction Form

Data Correction Form

5.4.5. Web Service Task

The next step in processing the mortgage application is to determine the applicant’s credit score. This application assumes an external Web Service that takes the applicant’s social security number and returns their credit score. The simple Credit Report Web Service has been created for this purpose.

From the left side of the palette, open Service Tasks and drag the WS service task onto the canvas. Rename the ws node to Credit Report and place it after the diverging XOR gateway so that the applicant’s credit score is requested after the mortgage application data is validated. Drag the end point of the existing valid sequence flow to this new node and draw a new sequence flow from this service task node to the script task.

So far, the only required process variable has been the application variable, which holds all the required data within it. At this point, proceed to create process variables representing the input and output of the Web Service. As a reminder, process variables are declared in the web process designer by clicking on the canvas background to get access to the process properties.

Figure 5.57. Additional Process Variables

Additional Process Variables

Next, click on the Credit Report task node and add the follow lines of code as actions to be executed before and after the node.

On entry:

kcontext.setVariable( "ssn", application.getApplicant().getSsn() );

On exit:

application.getApplicant().setCreditScore( creditScore );

As the code clearly states, the ssn process variable is set up from the application object for the purpose of the web service call and once the credit score is retrieved, the application object is updated with its value.

Edit the data output set of the web service task. The result is configured as a generic object by default; modify its standard type to Integer and remove the custom type, to better represent the returned credit score value. Also edit the data input set of the web service and configure the web service parameter as a standard Integer, which is the correct type for ssn.

Use the variable assignments of the task to configure the inputs and outputs. Values are given as Constants unless noted otherwise:

Figure 5.58. Web Service Task Data Assignments

Web Service Task Data Assignments

Save the process and provide a meaningful description for its repository revision. At this point, the project editor can be used to build and deploy the project and starting a process instance should result in a credit score being (mock) calculated by the web service, assuming this service is created and deployed as described below.

5.4.5.1. Credit Report Web Service

For the purpose of this reference architecture where the focus is on the BPM Suite, assume that an external web service provides the required information on the credit worthiness of mortgage application.

For the sake of simplicity, create a basic Web Service that takes an applicant’s social security number as its only input and returns their Credit Score as the result. Rather than recreating the service, if you would prefer, you can find a deployable .war file in the accompanying download for this document.

Creating a simple Web Service using JSR-181 and JSR-224 requires a simple Web Application with an empty web.xml file and an annotated Java class:

On entry:

package com.redhat.bpms.examples.mortgage;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public class CreditService
{
    @WebMethod
    public Integer getCreditScore(Integer ssn)
    {
        int lastDigit = ssn - 10 * ( ssn / 10 );
        int score = 600 + ( lastDigit * 20 );
        System.out.println( "For ssn " + ssn + ", will return credit score of " + score );
        return score;
    }
}

This class simply uses the last digit of the social security number to mock up a credit score. The web deployment descriptor remains empty:

<!--?xml version="1.0" encoding="UTF-8"?-->
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

</web-app>

Assuming that these two files are deployed in a standard web application structure and deployed as jboss-mortgage-demo-ws.war on a local service, the following address would be used to access this service:

http://localhost:8080/jboss-mortgage-demo-ws/CreditService?WSDL

5.4.5.2. Web Service Error Handling

So far, conceivable error conditions could have arisen from invalid input data and the validation rules resulting a human data correction have been an adequate response to such errors. Calling a web service introduces new risks. The external service may be down and nonfunctional for unexpected reasons. Various communication, network and server errors may result in an invalid response.

To catch potential errors from the Credit Report service task, open the Catching Intermediate Events set of tools from the web designer palette and drag the Error event onto the canvas. Drop this node on the lower boundary of the service task; the boundary of the service task turns green to indicate that the event node is being dropped on the correct spot:

Figure 5.59. Web Service Error Placement

Web Service Error Placement

The error event node catches any errors resulting from the execution of the service task. In a way, this node serves a purpose similar to the validation rules, in that once the error has been detected, the process provides a chance to inspect the error, remedy the situation and try again.

Add a data output variable to this boundary event node called nodeError. System errors results in a WorkItemHandlerRuntimeException, which will now be assigned to this new variable. Create a process variable called wsError and assign it the custom type of org.jbpm.bpmn2.handler.WorkItemHandlerRuntimeException. Open DataOutputAssociation on the error boundary event node and map from nodeError to wsError. This way, the thrown exception is made available to the process in the form of a process variable called wsError.

Figure 5.60. Web Service Data Output

Web Service Data Output

Create a new user task called Troubleshoot, provide the same as the name property, and assign it to the admin group. Draw a sequence flow from the error catching event to this user task.

Also place a new XOR gateway node before the Credit Report task and modify the existing Valid flow to connect to it. This new node then connects to the service task.

Now, connect the Troubleshoot user task to the new converging gateway. Similar to the data correction loop, this creates a troubleshooting loop where any errors from the Web Service call can be examined and corrected before looping and trying the call again. This is predicated on the Credit Report service being idempotent, as is the case here.

Figure 5.61. Web Service Error Flow

Web Service Error Flow

Create a process variable of the standard type of String and call it wsErrorStack. The entire exception stack from the original exception will be converted to String form and stored in this variable. Do this by creating an On Entry Action for the Troubleshoot task and entering the following Java code as one action:

java.io.StringWriter errorStackWriter = new java.io.StringWriter();
wsError.getCause().printStackTrace( new java.io.PrintWriter( errorStackWriter ) );
kcontext.setVariable( "wsErrorStack", errorStackWriter.toString() );

The cause of the system error is the actual exception that occurred during the web service invocation and in this case, its stack trace is retrieved and stored as a variable.

Create a Data Input variable for the Troubleshoot task and give it the name errorStack and standard type of String. In the task assignments, map from the process variable wsErrorStack to errorStack. This makes the stack trace of the thrown root exception available to the actor of this user task. To display the stack trace, create a task form for this user task.

Figure 5.62. Troubleshoot Data Input

Troubleshoot Data Input

Using the Graphic Modeler, choose to Add fields by type this time. This form only requires one field of type Long text; give the field the label of Web Service Error and call it something like errorStack. Set the size and height of the field to appropriate values for an exception stack (e.g., 200 and 10 respectively). Set the field as read-only and enter its input binding expression as errorStack, which is the name of the task variable.

Figure 5.63. Troubleshoot Task Form

Troubleshoot Task Form

This task form helps provide a technical administrator more information about the cause of the error that occurred while calling the external web service. The administrator can review this information and use it to troubleshoot and correct the problem, before completing the task and having the process retry the web service call.

5.4.6. Mortgage Calculation

5.4.6.1. Mortgage Calculation Process Model

Once the applicant’s credit score is determined, the next step is to assess the risk of the requested mortgage and determine the interest rate that can be offered to this applicant.

Such calculations are a natural fit for a rule engine. Using business rules to calculate the interest rate accelerates development and greatly reduces the cost of maintenance.

Once again, the only requirement is to insert the relevant objects into the rule engine’s working memory. The mortgage amount is also recalculated to make sure it is updated with the correct value, as the down payment is subject to change (in sections that follow):

application.setMortgageAmount(
    application.getProperty().getPrice() - application.getDownPayment() );

kcontext.getKnowledgeRuntime().insert( application.getApplicant() );
kcontext.getKnowledgeRuntime().insert( application.getProperty() );
kcontext.getKnowledgeRuntime().insert( application );

if( application.getAppraisal() != null )
    kcontext.getKnowledgeRuntime().insert( application.getAppraisal() );

This time the rules operate directly on the application object by setting its apr field to the calculated interest rate value. The last set of rules clean up the working memory by retracting all the existing objects.

The process simply adds a business rule task, using the above lines of code as its on entry actions and settings its ruleflow-group attribute to apr-calculation so that Mortgage Calculations rules execute.

5.4.6.2. Mortgage Calculation Rules

This simplified business model prices a mortgage by first calculating a minimum interest rate, based on only the length of the fixed-term mortgage (i.e., APR) and the applicant’s credit score. This is followed by a look at the down payment ratio and the APR is adjusted upward if less than 20% is provided. Finally, jumbo mortgages are identified and result in yet another potential increase in the mortgage APR.

Calculating the interest rate based on credit score and amortization is a natural tabular format and a great fit for a decision table. Create a guided decision table as a new item and call it Mortgage Calculation. Select to use the wizard and proceed to the next step. There is no need to import any Java types so once again, click next.

Choose Applicant and Application as the two fact patterns to use, as they hold the applicant’s credit score and selected amortization respectively.

Figure 5.64. Guided Decision Table Wizard

Guided Decision Table Wizard

In the next step, select each pattern, a field for that pattern, then create a constraint for that given field of the selected pattern.

Credit scores are considered in brackets so to designate a bracket, two separate constraints are required for the credit score where one defines the acceptable lower bound and the other, the upper bound.

Select Applicant and then creditScore, as its field. Click on the created condition template and complete it by declaring a column header of Credit Score >= to indicate that the provided values in the table are the lower bound. Set the operator to greater than or equal to. Set the value list based on credit score brackets: ,660,680,700,720,740

The table allows an analyst to easily create or update rules. By providing a value list, the application limits the credit score brackets to know values and generate a drop-down instead of a free-form text field. The preceding comma allows a blank value in the drop-down, which, when used, is equivalent to not specifying a lower bound for the credit score:

Figure 5.65. Credit Score Condition

Credit Score Condition

Select the creditScore field once again and click the double right arrow to add another condition based on this same field. This time the condition sets the upper bound value for the applicant’s credit score. Name the column header Credit Score < this time and choose the operator of less than. Use the same value list again to allow empty values.

Next, select the amortization field of Application and add a condition based on this field. Call the column header Fixed Mortgage Term and use the equal to operator. Set the value list to only allow acceptable amortizations: 10, 15, 30.

Click next to set the action corresponding with the condition. Selection Application as the pattern and then apr as its field. Click on the created action template under Chosen fields and enter the column header as Mortgage APR.

Figure 5.66. Decision Table Actions

Decision Table Actions

Skip the remaining steps, as there is no need to insert any facts and the columns may be left expanded.

Once the table is created, an important step is to set its ruleflow-group attribute to apr-calculation so that it is associated with the corresponding business rule task in the process. To do this, expand the decision table configuration by clicking the plus sign and select to add a new column. Choose the following option:

Add a new Metadata\Attribute column

This action creates the attribute group under options. Expand options and enter the ruleflow group as the default attribute for all table rows:

Figure 5.67. Adding Table Metadata

Adding Table Metadata

The generated table likely will require some manipulation to remove rows which can’t logically occur, as well as providing APR values for each entry.

At the time of writing, a known issue sometimes prevents the addition of metadata on a guided decision table, showing no entries under (options). Should you encounter this issue, the above section serves as an excellent means of getting familiar with decision tables, however, you can delete the guided table you’ve created and manually enter a DRL file to accomplish the same end-goal:

package com.redhat.bpms.examples.mortgage;

rule "Row 1 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 680 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 10.0 );
end

rule "Row 2 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 680 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 9.5 );
end

rule "Row 3 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 680 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 9.0 );
end

rule "Row 4 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 700 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 9.5 );
end

rule "Row 5 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 700 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 9.0 );
end

rule "Row 6 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 700 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 8.5 );
end

rule "Row 7 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 720 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 9.0 );
end

rule "Row 8 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 720 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 8.5 );
end

rule "Row 9 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 720 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 8.0 );
end

rule "Row 10 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 740 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 8.5 );
end

rule "Row 11 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 740 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 8.0 );
end

rule "Row 12 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 660 , creditScore < 740 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 7.5 );
end

rule "Row 13 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 700 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 8.0 );
end

rule "Row 14 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 700 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 7.5 );
end

rule "Row 15 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 700 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 7.0 );
end

rule "Row 16 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 720 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 7.5 );
end

rule "Row 17 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 720 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 7.0 );
end

rule "Row 18 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 720 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 6.5 );
end

rule "Row 19 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 740 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 7.0 );
end

rule "Row 20 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 740 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 6.5 );
end

rule "Row 21 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 680 , creditScore < 740 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 6.0 );
end

rule "Row 22 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 700 , creditScore < 720 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 6.5 );
end

rule "Row 23 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 700 , creditScore < 720 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 6.0 );
end

rule "Row 24 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 700 , creditScore < 720 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 5.5 );
end

rule "Row 25 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 700 , creditScore < 740 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 6.0 );
end

rule "Row 26 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 700 , creditScore < 740 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 5.5 );
end

rule "Row 27 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 700 , creditScore < 740 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 5.0 );
end

rule "Row 28 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 720 , creditScore < 740 )
    f2 : Application( amortization == 10 )
  then
    f2.setApr( 5.5 );
end

rule "Row 29 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 720 , creditScore < 740 )
    f2 : Application( amortization == 15 )
  then
    f2.setApr( 5.0 );
end

rule "Row 30 Mortgage Calculation"
  ruleflow-group "apr-calculation"
  dialect "mvel"
  when
    f1 : Applicant( creditScore >= 720 , creditScore < 740 )
    f2 : Application( amortization == 30 )
  then
    f2.setApr( 4.5 );
end

After calculating the base interest rate for a given credit score and amortization, the immediate next step in the calculation is to consider the down payment. The base interest rate assumes an industry-standard down payment of twenty percent. Anything below that results in a higher interest rate.

Mortgage calculation takes place before appraisal to avoid the unnecessary cost of property appraisal for an applicant that is not otherwise qualified. However even if the application appears to qualify, property appraisal may result in an assessment of a property value that is significantly lower than the transaction price. Such an assessment may impact the down payment ratio and require a renewed calculation, this time based on the appraised value of the property.

Create two separate rules to cover the evaluation of the down payment in the two separate cases of before and after appraisal.

Call the first guided rule as follows: Low Down Payment before Appraisal

Figure 5.68. Low Down Payment Rule

Low Down Payment Rule

To create this rule as shown above, set the first constraint to: The following does not exist

Proceed to click on the generated phrase and select Appraisal the fact type. This composite constraint states that this rule is only applicable if an appraisal has not yet been performed.

Click the plus icon again to create a new pattern and select Property, then configuring it to have a variable name of property so that it can be referenced in the consequence of the rule.

Create a third pattern for Application and set a restriction on its mortgageAmount field to be greater than the following formula: property.price * 8 / 10

If the mortgage amount is greater than 80% of the property price, it follows that the down payment has been less than 20% of the total price.

Show options and add the ruleflow-group attribute, setting it to apr-calculation.

Also add the salience attribute and give it a value of -3. This ensures that the base APR calculation rules in the decision table, with a default salience of 0, have already executed before this rule.

Finally, add the no-loop attribute and check the corresponding box. This attribute avoids an infinite rule where the update of the application by this rule, through an APR surcharge, may trick the rule engine into thinking that something has changed and this rule must be reevaluated. In the case of this particular rule, it is nothing but a safety precaution.

The action of this rule is more complicated than any previous one. The guided rule facilities make it easy to author and update rules but are often not appropriate for more difficult technical syntax. Luckily, adding free-form DRL is directly supported in the guided rule editor. Enter the following DRL as the first part of this rule’s consequence:

double ratio = application.getMortgageAmount().doubleValue() /
        property.getPrice().doubleValue();
int brackets = (int)((ratio - 0.8) / 0.05);
brackets++;
double aprSurcharge = 0.75 * brackets;

At this point, with the rule executing, it is known that the ratio of the mortgage amount to the total property sale price is higher than 80% but this first line calculates this ratio.

While any ratio greater than 0.8 (as is certainly the case here) triggers an APR surcharge, the amount of the surcharge is constant for every little bracket of five percent. A ratio between 80% and 85% triggers an APR surcharge of 0.75 while the next bracket, between 85% and 90%, doubles the surcharge. The bracket number is calculated above and reindexed to 1 before being multiplied by 0.75 to determine the exact applicable surcharge.

Finally, add a second action to: Change field values of application…​

Select the apr field and set it to the following formula: application.getApr() + aprSurcharge

Evaluating the sufficiency of the down payment after an appraisal is very similar. This is only necessary if the appraisal has resulted in an assessment of a value for the property that is lower than the sale price. In this case, the mortgage amount remain the same (down payment subtracted from the sale price) but it needs to be lower than 80% of the appraised value. In other words, it is being divided by a smaller denominator.

The rule is otherwise similar:

Figure 5.69. Low Down Payment after Appraisal

Low Down Payment after Appraisal

The next potential adjustment to the calculated mortgage interest rate concerns jumbo loans. For simplicity, this business entity assumes a uniform conforming loan threshold of $417,000. Any mortgage amount above this threshold is considered a jumbo loan and subject to an APR surcharge of 0.5.

This rule is give a salience of -5 and applied after potential down payment surcharges.

Figure 5.70. Jumbo Mortgage Rule

Jumbo Mortgage Rule

Once again, the no-loop attribute has been added and selected as a precaution.

As was the case with validation, it is also important here for the rules to clean up after themselves. Create a new DRL and call it: Retract Facts After Calculation

The rules to find and retract the fact types are almost identical:

package com.redhat.bpms.examples.mortgage;

rule "Retract Applicant after Calculation"
    dialect "mvel"
    ruleflow-group "apr-calculation"
    salience -10
    when
        fact : Applicant( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end

rule "Retract Application after Calculation"
    dialect "mvel"
    ruleflow-group "apr-calculation"
    salience -10
    when
        fact : Application( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end

rule "Retract Appraisal after Calculation"
    dialect "mvel"
    ruleflow-group "apr-calculation"
    salience -10
    when
        fact : Appraisal( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end
rule "Retract Property after Calculation"
    dialect "mvel"
    ruleflow-group "apr-calculation"
    salience -10
    when
        fact : Property( )
    then
        retract(fact);
        System.out.println("Executed Rule: " + drools.getRule().getName() );
    end

5.4.7. Qualify Borrower

While the lending risk is already reflected in the calculated interest rate, there are also cases where the lender refuses to provide the applicant with a mortgage. One such case is the front-end ratio for the mortgage application exceeds the 28% threshold.

Calculating the front-end ratio is a simple matter of determining the monthly mortgage payment (this process ignores other housing costs) and dividing it by the applicant’s income. This requires relatively simple arithmetics that is not a natural fit for a rule engine. For simplicity, this calculation can be performed in a script task.

Create a process variable called borrowerQualified of the standard type of Boolean. The script task will set this boolean variable to true or false, indicating whether the applicant is qualified for the mortgage based on the APR and front-end ratio.

Create a Script Task called Qualify Borrower and use the following code snippet as its Script:

System.out.println("Qualify Borrower");
double monthlyRate = application.getApr() / 1200;
double tempDouble = Math.pow(
        1 + monthlyRate, application.getAmortization() * 12);
tempDouble = tempDouble / (tempDouble - 1); tempDouble = tempDouble * monthlyRate * application.getMortgageAmount();
System.out.println("Monthly Payment: " + tempDouble);
boolean qualified =
        (application.getApplicant().getIncome() / 12 * 0.28 > tempDouble);
kcontext.setVariable("borrowerQualified", Boolean.valueOf(qualified));

Place the new script task after the mortgage calculation business rules:

Figure 5.71. Qualify Borrower Script Task

Qualify Borrower Script Task

5.4.8. Increase Down Payment

5.4.8.1. Increase Down Payment Process Model

In an effort to avoid losing a business opportunity, the process explores alternative ways to qualify the mortgage applicant. One simple solution is to request a larger down payment.

Create a diverging XOR gateway after (preferably above) the Qualify Borrower script node. From this gateway, create two distinct sequence flows to handle the cases where the borrower may have been qualified or not qualified based on the monthly payment calculation.

Draw a sequence flow to the right and attach it to following nodes, at this point a node that simply prints out the application object and continues to terminate the process. Name this sequence flow Qualified and set a Java condition expression to verify that the borrowerQualified process variable is true:

Figure 5.72. Qualified Borrower

Qualified Borrower

Create a user task node on the left side of this gateway and draw a second sequence flow to it. Call this new sequence flow Not Qualified and set the condition expression language to Java again. The condition expression should look for borrowQualified to be false this time:

return borrowerQualified.booleanValue() == false;

The new user task will allow the business to contact the applicant and request a larger down payment to avoid declining the mortgage application. Once again, the task is assigned to the broker group and the input and output variables are taskInputApplication and taskOutputApplication, which are both mapped to the application process variable. The task form for the Increase Down Payment task uses the application object to render the required data on the screen and updates the downPayment field of its output application variable.

5.4.8.2. Increase Down Payment Task Form

The task form to increase the down payment is very simple and consists of only four fields.

The sale price of the property is included, mostly as a reminder. The request to increase down payment is always due to a rejection of the original mortgage application. In the regular turn of event, the mortgage interest rate is calculated based on various factors and is in turn used to derive the monthly payment. The mortgage may not qualify and a higher down payment requested, simply because this calculated interest rate is too high. In another scenario, the appraisal of the property may result in a value that is lower than the sale price.

Based on these two common causes, the mortgage apr and the appraisal value (if appraisal is even performed yet) are presented as part of the task form.

These three fields (property price, appraisal value and mortgage APR) should be marked as read-only so that they cannot be modified by the task.

The fourth and final field of this task form is the down payment itself, which may be updated by the task.

Instead of using subforms, this task form utilizes Add fields by type and Short text inputs, thereby directly navigating to and referencing the values corresponding to the form fields. The input binding values for property price, appraisal value and mortgage APR respectively are taskInputApplication/property/price, taskInputApplication/appraisal/value and taskInputApplication/apr. All three fields are marked read-only and their output binding field is left blank.

Down payment has taskInputApplication/downPayment as its input binding and taskOutputApplication/downPayment as its output binding.

Figure 5.73. Down Payment Task Form

Down Payment Task Form

5.4.8.3. Limiting Increase Down Payment Loops

Once the down payment has been revised, mortgage calculations need to be renewed to determine if the applicant is now qualified. Create a new converging gateway between the credit report and mortgage calculations nodes to allow the process to join that flow and create a new loop to potentially continually revise the down payment amount upwards until the mortgage is qualified.

Figure 5.74. Down Payment Process Flow

Down Payment Process Flow

While the loop allows the down payment to be increased continually without setting an arbitrary restriction on the number of loops, it also effectively removes the possibility of not qualifying a mortgage application. In other words, any application that is not qualified results in an infinite number of attempts to increase the down payment, even if the applicant is neither willing or able to do so.

A simple solution is to inspect the down payment and detect whether it has in fact been increased. Once the task fails to increase the down payment, a separate path may be taken to avoid an infinite loop scenario. To detect an increase in the down payment create a new process variable of standard type Integer and call it downPayment. On entry to the user task to increase down payment, add a new action that sets this process variable to the down payment value before its potential update by the user:

kcontext.setVariable( "downPayment", application.getDownPayment() );

Now that a different path can be taken for a mortgage that cannot be qualified, it would also be better process design to provide two distinct paths of mortgage approval and denial which would include two separate termination flows for the process.

Instead of immediately merging back into the main process flow, place a diverging XOR gateway after the user task to increase down payment. Create two outgoing sequence flows from this new gateway, where one merges back into the main process flow and gets back into the loop, but the other goes to a new script task node called Deny Mortgage and a new End Event after that. For the sake of consistency, also rename the previously created printing script task to Approve Mortgage.

The choice of sequence flow is based on whether the down payment was in fact increased or not. Accordingly, name the two sequence flows Yes and No. Use a Java condition expression that compares the previously recorded value of the down payment with its potentially updated value:

Figure 5.75. Deny Mortgage Process Flow

Deny Mortgage Process Flow

This way, the loop continues while the applicant increases the down payment and repeated as long as it’s not enough to qualify them, or until such point that the applicant declines to raise the down payment amount any further.

5.4.9. Financial Review

5.4.9.1. Financial Review Process FlowModel

While the business process frequently solicits input from users, all the decisions so far have been automated. It is common for most business processes to have some sort of manual override. In this example, a manager may have the authority to approve a mortgage application that does not meet the standard criteria. As a last resort before declining the application, provide a user task form assigned to the manager group that allows a manager to approve the mortgage.

Create a process variable called brokerOverride of the standard type of Boolean. Once again, map the application process variable to taskInputApplication, used by the task form, designed as instructed below, but this time allow the output to only be a boolean variable called brokerOverrideTaskOutput that maps back to brokerOverride.

Use this final decision in a diverging XOR gateway to decide whether to still proceed to decline the mortgage, or to approve it. Approving it means another converging gateway to merge the approval resulting from regular qualification with that of the manual override:

Figure 5.76. Broker Override Process Flow

Broker Override Process Flow

5.4.9.2. Financial Review Task Form

The financial review task form allows a manager to review a declined mortgage application and potentially override the decision. The information displayed along with the task to enable such a decision includes all the financial data pertaining to the mortgage application, including both the data provided by the applicant and the rates and values computed by the process. This includes Property Sale Price, Appraised Value, Down Payment, Amortization, Mortgage APR, Credit Score and Annual Income. All of these fields are marked as read-only and much like the Increase Down Payment task form, they are linked directly to the field nested within the application object instead of using a subform.

The decision to override the process and approve the mortgage application is made through this form through a simple checkbox that is mapped to a task variable in its output binding expression. This boolean variable is mapped by the task to an equivalent process variable which is used in a gateway to determine if the mortgage should be approved.

Figure 5.77. Broker Override Task Form

Broker Override Task Form

5.4.10. Appraisal

5.4.10.1. Appraisal Process Model

Notice that up to this point, mortgage calculations have been based on the down payment as a ratio of the transaction price. One missing important step in this process is the appraisal of the property.

Property appraisal can be costly, so a well-designed business process delays incurring such a cost until other factors have all been taken into account. For this reason, the process adds the appraisal task as a last step before approving the mortgage.

However, appraisal does not act in a silo and much like most other steps of the business process, it can also result in a loop that affects other decisions. For example an applicant may easily qualify but the property maybe appraised at a lower value than the sale price. This necessitates a new round of calculation and as a result, the applicant may no longer qualify and need to increase the provided down payment. It may also be that the application had only been approved based on a higher down payment and/or a manager’s override, but the appraisal throws an additional wrinkle into the mix, requiring yet further increases in the down payment amount or a renewed managerial override.

Fortunately, tying this additional requirement into the process is not complicated. The modularity of BPM and rules allow us to add this additional step with relative ease.

Create a new diverging XOR gateway before Approve Mortgage. For better modeling readability, give this gateway a name of Appraised? and then create a Yes sequence flow that connects to a converging gateway and approves the mortgage afterwards. To check and see if the property has already been appraised, simply verify that the appraisal field of the application variable is not null:

Figure 5.78. Appraised Process Flow

Appraised Process Flow

The No sequence flow goes to a new user task called Appraisal, with a task form designed as described in the corresponding section, the Appraisal form. The appraisal task is assigned to the appraiser group and simply takes the application as its input and updates it as its output. The only part of the application that may be updated is the value field of the appraisal object within application.

Once appraisal is performed, compare the appraised value with the sale price of the property:

return (application.getAppraisal().getValue() >=
                        application.getProperty().getPrice());

Place another diverging gateway after the appraisal task and creating two sequence flows leaving it, Sufficient Appraisal if appraisal value is at least the sale price of the property, and Low Appraisal if it is appraised at a lower price:

Figure 5.79. Insufficient Appraisal Process Flow

Insufficient Appraisal Process Flow

In the case of a low appraisal, mortgage calculations would need to be repeated and there is a possibility that an otherwise qualified mortgage application would be declined unless there is further down payment increases or a manager override.

Business rules are designed to take appraisals into account, as demonstrated in the Figure shown earlier in the chapter titled Low Down Payment after Appraisal.

5.4.10.2. Appraisal Task Form

The appraisal of the property happens only once in the process and is never updated. Accordingly, the appraisal value has no input binding and starts as blank before being entered into the task and updated within the appraisal field of the application object.

The appraisal field of the task may both be modeled as a simple field, or as a subform for the custom appraisal type. Modeling a simple field is easier and faster to do, while the subform is reusable and the associated extra effort pays off in terms of consistency and future ease of use.

Figure 5.80. Appraisal Task Form

Appraisal Task Form

5.4.11. Swimlanes and Business Continuity

The mortgage application business process includes a total of five user tasks, with each being assigned to a different group while Data Correction and Increase Down Payment are both assigned to the same group. Assigning tasks to groups has the advantage of avoiding tight coupling between individuals and business processes that may need attention outside that individual’s working hours. In this model, any member of a group of users is able to view all assigned tasks and claim a task to work on. Further configuration makes it possible to notify group members or make more advanced assignment decisions.

In a process like this where the same task may be executed multiple times, or even in a case where a mortgage broker might need to contact the same customer once to correct data and another time to request a higher down payment, there is great business value in having the same group member handle both tasks.

Swimlanes allow a task to be assigned to a group, but to undergo assignment a single time for each business process instance. In other words, once a task in a swimlane has been claimed by an actor, all other instances of the same task or other tasks in the same swimlane will automatically be assigned to that actor again for the lifetime of the process instance. This avoids the situation where a user will have to deal with a different mortgage broker at each turning point, for the same mortgage application.

The user tasks in this process are as follows. A single swimlane will be used for the two tasks assigned to the broker group with a second swimlane used for the Financial Review task for the manager.

Table 5.6. User Tasks & Swimlanes

User Task NameGroup AssignmentSwimlane

Data Correction

broker

yes

Increase Down Payment

broker

yes

Financial Review

manager

yes

Troubleshoot

admin

no

Appraisal

appraiser

no

To create a swimlane, open the Swimlanes group from the palette and drag and drop the Lane onto the canvas. Resize the lane as appropriate to cover the two broker tasks and their adjacent nodes. Double-click the lane to give it name; call the lane broker to make clear that associated user activities will be performed by a member of the broker group.

By default, nodes are placed one on top of each other in the order in which they are placed in the canvas. Based on this behavior, the swimlane node covers all the process activity as it is placed and resized in the process. Select the lane and use the toolbar menu to send it to the back. Resize the lane in a way that its borders are around the nodes, so that they remain visible:

Figure 5.81. Broker Swimlane

Broker Swimlane

Finally, add a second swimlane above the first encompassing Financial Review for managers.

5.4.12. Final Process Model

The final business process model is as follows:

Figure 5.82. Final Process Model

Final Process Model