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"
}]
Stay tuned! 🚀