Flowable ft. Micronaut - Versioning and Audit

Knowledge is power and Flowable has a lot of it.

Brief

We need to investigate Flowable versioning and audit capabilities.

Findings

Versioning

DMN files do not support versioning.

If Flowable is used locally, then each time the application restarts, Flowable resets its tables. In this case the versioning of decision tables might be done using Git's versioning.

If deployed as a separate instance, Flowable does support versioning. It creates decision tables based on decision files, so updating an existing rule through a new deployment would increase the rule's version. More explicitly, Flowable keeps a column named version on a table level that gets increased with new deployments of the same rule.

Audit

Audit info can be retrieved through making HTTP requests to the Flowable's REST API or calling methods on the DmnEngine. Next, I will show the audit results and how can they be accessed using both methods.

Audit details provided by Flowable include:

  • decision table key
  • decision version
  • list of rule executions
  • in-depth details for every execution
    • decision hit policy
    • input conditions that were valid/invalid for each rule
    • execution time
    • decision result

Using the REST API

  • All rule executions

GET /flowable-rest/dmn-api/dmn-history/historic-decision-executions

 [{
            "id": "a5d94f79-8b0d-11eb-9034-0242ac110002",
            "url": "http://localhost:8080/flowable-rest/dmn-api/dmn-history/historic-decision-executions/a5d94f79-8b0d-11eb-9034-0242ac110002",
            "decisionDefinitionId": "9efff3a8-8b0a-11eb-9034-0242ac110002",
            "deploymentId": "9ef4f726-8b0a-11eb-9034-0242ac110002",
            "activityId": null,
            "executionId": null,
            "instanceId": null,
            "scopeType": null,
            "failed": true,
            "startTime": "2021-03-22T12:53:49.763+00:00",
            "endTime": "2021-03-22T12:53:49.810+00:00",
            "tenantId": "",
            "decisionKey": "RULES",
            "decisionName": "Rules",
            "decisionVersion": "1"
}]
  • Details of a single execution

GET /flowable-rest/dmn-api/dmn-history/historic-decision-executions/6f83a55f-8b13-11eb-9034-0242ac110002/auditdata

{
    "decisionKey": "RULES",
    "decisionName": "Rules",
    "decisionVersion": 2,
    "hitPolicy": "UNIQUE",
    "startTime": "2021-03-22T13:35:15.631+00:00",
    "endTime": "2021-03-22T13:35:15.642+00:00",
    "inputVariables": {
        "input1": "INPUT_1",
        "input2": "INPUT_2"
    },
    "inputVariableTypes": {
        "input1": "string",
        "input2": "string"
    },
    "decisionResult": [
        {
            "rule_id": "==",
            "ouput1": "OUTPUT_1",
            "output2": "OUTPUT_2"
        }
    ],
    "multipleResults": false,
    "decisionResultTypes": {
        "ouput1": "string",
        "output2": "string"
    },
    "ruleExecutions": {
        "1": {
            "startTime": "2021-03-22T13:35:15.632+00:00",
            "endTime": "2021-03-22T13:35:15.634+00:00",
            "ruleNumber": 1,
            "valid": true,
            "conditionResults": [
                {
                    "id": "inputEntry_inputExpression_1_1",
                    "result": true
                },
                {
                    "id": "inputEntry_inputExpression_2_1",
                    "result": true
                }
            ],
            "conclusionResults": [
                {
                    "id": "outputEntry_outputExpression_1_1",
                    "result": "OUTPUT_1"
                },
                {
                    "id": "outputEntry_outputExpression_2_1",
                    "result": "OUTPUT_2"
                }
            ]
        },
        "2": {
            "startTime": "2021-03-22T13:35:15.634+00:00",
            "endTime": "2021-03-22T13:35:15.635+00:00",
            "ruleNumber": 2,
            "valid": false,
            "conditionResults": [
                {
                    "id": "inputEntry_inputExpression_1_2",
                    "result": false
                },
                {
                    "id": "inputEntry_inputExpression_2_2",
                    "result": true
                }
            ],
            "conclusionResults": []
        }
    },
    "failed": false,
    "strictMode": true
}

As you can see, the first rule matched the input, hence the valid property set to true, whereas the second rule did not match because of the first input:

{
    "id": "inputEntry_inputExpression_1_2",
    "result": false
}

Using the Flowable Engine

In order to retrieve the audit info through the Flowable Engine we need to set the historyEnabled property of the DmnEngine bean to true. (Bear in mind that this can lead to performance issues).

@Factory
@RequiredArgsConstructor
public class FlowableConfiguration {

    @Value("${flowable.rules}")
    private String ruleFile;

    @Value("${flowable.datasource.url}")
    private String datasourceUrl;

	@Bean
    public DmnEngine dmnEngine() {
        DmnEngine dmnEngine = new StandaloneDmnEngineConfiguration()
                .setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE)
                .setJdbcUrl(datasourceUrl)
                .setStrictMode(false)
                .setHistoryEnabled(true)
                .buildDmnEngine();

        dmnEngine.getDmnRepositoryService()
                .createDeployment()
                .addClasspathResource(ruleFile)
                .deploy();
        return dmnEngine;
    } 
}

To get the same details from the GET /flowable-rest/dmn-api/dmn-history/historic-decision-executions request:

@Singleton
@RequiredArgsConstructor
public class FlowableService {

    private final DmnEngine dmnEngine;

    public List<DmnHistoricDecisionExecution> getAuditDetails(String decisionKey) {
        return dmnEngine.getDmnHistoryService()
        .createHistoricDecisionExecutionQuery()
        .decisionKey(decisionKey)
        .list()
    }
}

The details for each rule executions in the form of the result of

GET /flowable-rest/dmn-api/dmn-history/historic-decision-executions/6f83a55f-8b13-11eb-9034-0242ac110002/auditdata

are stored in the executionJson field of the DmnHistoricDecisionExecution object.

Bonus

Other details of Flowable's deployments can be retrieved through its REST API. More info here: https://www.flowable.com/open-source/docs/bpmn/ch15-REST/

Example ( get list of decision tables)

GET /flowable-rest/dmn-api/dmn-repository/decision-tables

[{
            "id": "b3dsac8c-8b0d-11eb-9034-0242ac110002",
            "url": "http://localhost:8080/flowable-rest/dmn-api/dmn-repository/decisions/b3dsac8c-8b0d-11eb-9034-0242ac110002",
            "category": null,
            "name": "Rules",
            "key": "RULES",
            "description": "Rule table",
            "version": 2,
            "resourceName": "rules.dmn",
            "deploymentId": "b3dsac8c-8b0d-11eb-9034-0242ac110002",
            "tenantId": "",
            "decisionType": "decision_table"
}]

💡
Don't miss out on more posts like this! Susbcribe to our free newsletter!
💡
Currently I am working on a Java Interview e-book designed to successfully get you through any Java technical interview you may take.
Stay tuned! 🚀