waltz icon indicating copy to clipboard operation
waltz copied to clipboard

Survey Workflow: design

Open jessica-woodland-scott-db opened this issue 3 years ago • 4 comments

  • [ ] setup ddl
    • [ ] migrate current entity_workflow_definition' to 'workflow_definition' (adding external id and owner, status etc...)
    • [ ] introduce 'workflow_state' table and populate with the distinct list of state values form the existing 'entity_workflow_state' table
    • [ ] migrate 'entity_workflow_state' to 'workflow_instance' (state -> state_id etc)
    • [ ] migrate 'entity_workflow_transition' to 'workflow_transition' (drop entity columns, use instance_id and state_id).
    • [ ] update existing internal jobs to use the new tables
  • [ ] Update / recreate model for new workflow tables
  • [ ] Workflow job
    • [ ] Externalise rules
    • [ ] #5812
    • [x] Define predicates for rules
    • [ ] Define initialisation rules / predicates
    • [ ] Define 'side-effects' for rules e.g. issue surveys or update assessment
  • [ ] Workflow admin screens

Users would like the ability to use a workflow system to drive the issuance of surveys with a view to updating one or more assessments in waltz.

For example, an architectural review process: This workflow is used to track architectural governance compliance of change initiatives.

2 assessments will be used:

  • Outcome assessment
  • Workflow status assessment

An external batch job determines which change initiatives are in scope and sets the outcome assessment to 'In Scope'. A workflow job will wait for assessments in this state and issue a "scoping" survey and update the workflow status assessment to be 'Pending Completion'. etc.... Once the survey is approved the initial assessment is updated to reflect the end state.


Workflow Support

Over the past year we have been using Waltz to support custom workflows. These workflows are orchestrated using bespoke jobs and are not easily reusable. We believe a small set of reusable, composable functionality could simplify the creation future workflows.

An Example workflow

Examples of these bespoke workflow processes have been in areas such as Records Management. The Records Management process coordinates multiple surveys with the goal of providing assessment and rating values against the subject application.

Scoping Survey

The process starts with a job issuing a Scoping survey to determine what, if any, record classes a subject application manages. The scoping survey is issued to the application owner and is reviewed by a representative for the Records Management team. Once approved the record classes are used to determine the next step:

  • No Records:

    • a 'does not contain records' assessment is made against the application and the process ends
  • Some Records:

    • a 'contains records' assessment is made against the application

for each declared record class a provisional rating is stored as a Records Management Taxonomy Rating for that application

Detail Survey

A secondary job periodically checks the Records Management Taxonomy Rating looking for provisional ratings. For each rating found a new Detailed Survey is issued against the application with a similar set of owners and approvers. As each of the Detail Surveys are approved then the taxonomy ratings are either removed, or the rating moves from provisional to Confirmed.

Observations

The two jobs which issue the Scoping and Detail surveys as intentionally decoupled. They use a combination of Assessments and Measurable (Taxonomy) Ratings to have small, controlled amount of shared state.

Due to this separation, Records Management Administrators can override values in the ratings or assessments and the workflow will act correctly.

For example, an administrator could manually store some provisional record ratings against an application and the corresponding detail surveys would be issued without the need for a scoping survey. Similarly, if an administrator want to manually set some confirmed ratings against an application any outstanding detail survey will be withdrawn.

Towards configurable workflows

If we take the above example and also consider some other use-case we can start cataloguing a set of functionality needed to deliver a flexible workflow solution.

  • State management
    • Use of assessments and/or taxonomy ratings to track workflow state
  • Triggers - when do we need to revaluate the workflow and potentially take action
    • submission - when a survey is submittedm but not yet approved
    • appproval - when a survey has been approved
    • rejection - when a survey has been rejected by the approver
    • withdrawn - when a survey has been withdrawn by the owner or approver
    • timed - e.g. when a workflow has been in a given state for a set period of time
  • Predicates - evaluations to determine which action to take
    • Waltz contextual - information about the subject (i.e. investment state, other assessments)
    • survey responses - answers given in response to survey questions
    • external contextual - information obtained from non-Waltz sources (unlikely to be in phase 1)
  • Actions - changes to effect outside of the workflow engine
    • update assessment/s
    • update rating/s
    • update approvers - use case is to allow differing reviewers based on survey answers (i.e. route to the most qualified reviewers)
    • issue additional surveys
    • add/remove from groups
    • external actions - probably simlpy write a basic message to persistent queue for an external job to process

There are the following tables in Waltz which could be modified to accommodate these changes:

  • entity_workflow_definition
  • entity_workflow_state
  • entity_workflow_transition

Most likely by creating a new table to track the state, and renaming entity_workflow_state to entity_workflow_instance

Proposed tables:

workflow_definition

Attribute Type Description
id Long Sequential identifier
name String name
description String description
external_id String external id
created_at Timestamp workflow first created
created_by String user to create workflow
owning_role String which users can modify this workflow
status String ACTIVE / DRAFT / REMOVED

workflow_state

Attribute Type Description
id Long identifier
workflow_defn_id Long FK to workflow_definition
name String name
description String description
external_id String Description
state_kind String PARKED (Did not fulfil any starting predicate) / IN_PROGRESS /COMPLETED

workflow_instance

Attribute Type Description
id Long Sequential identifier
workflow_definition_id Long FK to workflow definition
state_id Long state of this instance
parent_entity_id Long Entity this workflow is acting on
parent_entity_kind String Entity this workflow is acting on
external_id String external id
created_at Timestamp workflow first created
created_by String user to create workflow
last_updated_at Timestamp when was this instance last updated
last_updated_by String user to last update this instance

Example Rules

Records Mgmt

Initialisation

switch 
  case:
	- Pred: App in development && has no survey issued && is not euda
	- State: INITIAL_SURVEY_ISSUED/IN_PROGRESS
	- Side-effect: issueSurvey("INIT")
   default:
	 - State: NOT_REQUIRED/PARKED

Transitions

    HasRecords
		- InitialState: INITIAL_SURVEY_ISSUED
		- Pred: hasApprovedSurvey("INIT") && surveyAnswer("INIT", "Q1") == "HAS_RECORDS"
		- State: DETAIL_SUREVY_ISSUED/IN_PROGRESS
		- Side-effect: issueSurveyToAllEntitiesListedInSurvey("DETAIL", "INITIAL", "Q3") and updateAssessment("HAS_RECORDS", "YES")
	
	DoesNotHaveRecords
		- InitialState: INITIAL_SURVEY_ISSUED
		- Pred: hasApprovedSurvey("INIT") && surveyAnswer("INIT", "Q1") == "DOES_NOT_HAVE_RECORDS"
		- State: NO_RECORDS/COMPLETED
		- Side-effect: updateAssessment("HAS_RECORDS", "NO")
		
	DetailSurveyComplete
		- InitialState: DETAIL_SUREVY_ISSUED
		- Pred: hasApprovedSurvey("DETAIL")
		- State: DETAIL_SURVEY_COMPLETED/COMPLETED
		- Side-effect: updateRatingsFromSurveyQuestion("RECORDS", "12", "YES")
```		
		
		
## Arch Gov

**Initialisation**

	switch 
		case:
			- Pred: CI is not retired and hasAssessment("ARCH_SCOPE") == 'IN_SCOPE' (from clarity checks)
			- State: DISCOVERY_SURVEY_ISSUED/IN_PROGRESS
			- Side-effect: issueSurvey("DISCOVERY")
		default:
			- State: NOT_REQUIRED/PARKED

**Transitions**

    NoArchChange
		- InitialState: DISCOVERY_SURVEY_ISSUED
		- Pred: hasApprovedSurvey("DISCOVERY") && surveyAnswer("DISCOVERY", "Q1") == "No - No Architectural Change"
		- State: DISCOVER_SURVEY_NO_ARCH_CHANGE/COMPLETED
		- Side-effect: updateAssessment("ARCH_SCOPE", "DESCOPED")
		
    ArchChange
		- InitialState: DISCOVERY_SURVEY_ISSUED
		- Pred: hasApprovedSurvey("DISCOVERY") && surveyAnswer("DISCOVERY", "Q1") == "Yes"
		- State: ARCH_REVIEW_SURVEY_ISSUED/IN_PROGRESS
		- Side-effect: issueSurvey("ARCH_REVIEW")
		
	DiscoveryOverdue
		- InitialState: DISCOVERY_SURVEY_ISSUED
		- Pred: !hasApprovedSurvey("DISCOVERY") && monthsSinceLastUpdate > 6
		- State: DISCOVERY_SURVEY_OVERDUE/IN_PROGRESS
		- Side-effect: issueReminder()
		
	 ArchChangeFromOverdue
		- InitialState: DISCOVERY_SURVEY_OVERDUE
		- Pred: hasApprovedSurvey("DISCOVERY")
		- State: DISCOVERY_SURVEY_ISSUED/IN_PROGRESS		

	ArchReviewCompleted
		- InitialState: ARCH_REVIEW_SURVEY_ISSUED
		- Pred: hasApprovedSurvey("ARCH_REVIEW")
		- State: ARCH_REVIEW_COMPLETED/COMPLETED
		- Side-effect: updateAssessment("ARCH_SCOPE", getValueFromSurveyQuestion("ARCH_REVIEW", "Q4"))
		

Arch Gov (using context variables)

Context

name lookupFn example
subject this is implicitly available {extId: 'INV123', name: "Move to cloud", status: "ACTIVE" }
scoping_assessment assessment("ARCH_IN_SCOPE") IN_SCOPE
approval_survey survey_info("DISCOVERY") { status: 'APPROVED', lastUpdate: '2021-09-15', ... }
review_survey survey_info("ARCH_REVIEW") { status: 'APPROVED', lastUpdate: '2021-12-25', ... }
scope_answer survey_answer("DISCOVERY", "Q1") 'Yes'
survey_answers survey_answers("DISCOVERY") {Q1: 'Yes'', Q2: ['foo', 'baa'], ,,, }
name lookupRef example
subject this is implicitly available {extId: 'INV123', name: "Move to cloud", status: "ACTIVE" }
scoping_assessment {kind: "assessment", ext: "ARCH_IN_SCOPE"} IN_SCOPE

survey_answers.Q1.value === 'Yes' or scope_answer.value === 'Yes'

The above shows examples of direct vars (i.e. scope_answer) and aggregate vars (survey_answers).
Not sure which will be better yet, suspect direct vars will be clearer but the extra setup may offest this advantage. There is also a tension around performance, aggregate vars are great if lots of parts are used, but are inefficient if only one part is needed.
In opposition direct vars are efficient if only one or two are needed, if lots are needed from the same parent entity then aggregates would probably be better. This may be mitigated by grouping similar lookupFns (like the approach taken in Report Grids)

Using the direct approach, the arch gov definition would look like:

Initialisation

switch 
	case:
		- Pred: subject.status == 'ACTIVE' && scoping_assessment == 'IN_SCOPE' (from clarity checks)
		- State: DISCOVERY_SURVEY_ISSUED/IN_PROGRESS
		- Side-effect: issueSurvey("DISCOVERY")
	default:
		- State: NOT_REQUIRED/PARKED

Transitions

NoArchChange
	- InitialState: DISCOVERY_SURVEY_ISSUED
	- Pred: approval_survey.status == 'APPROVED' && scope_answer == "No - No Architectural Change"
	- State: DISCOVER_SURVEY_NO_ARCH_CHANGE/COMPLETED
	- Side-effect: updateAssessment("ARCH_SCOPE", "DESCOPED")
	
ArchChange
	- InitialState: DISCOVERY_SURVEY_ISSUED
	- Pred: approval_survey.status == 'APPROVED' && scope_answer == "Yes"
	- State: ARCH_REVIEW_SURVEY_ISSUED/IN_PROGRESS
	- Side-effect: issueSurvey("ARCH_REVIEW")
	
DiscoveryOverdue
	- InitialState: DISCOVERY_SURVEY_ISSUED
	- Pred: !approval_survey.status == 'APPROVED' && approval_survey.issuedOn > fromNow(6, "months")
	- State: DISCOVERY_SURVEY_OVERDUE/IN_PROGRESS
	- Side-effect: issueReminder()
	
 ArchChangeFromOverdue
	- InitialState: DISCOVERY_SURVEY_OVERDUE
	- Pred: approval_survey.status == 'APPROVED'
	- State: DISCOVERY_SURVEY_ISSUED/IN_PROGRESS		

ArchReviewCompleted
	- InitialState: ARCH_REVIEW_SURVEY_ISSUED
	- Pred: review_survey.status == 'APPROVED'
	- State: ARCH_REVIEW_COMPLETED/COMPLETED
	- Side-effect: updateAssessment("ARCH_SCOPE", getValueFromSurveyQuestion("ARCH_REVIEW", "Q4"))
	

davidwatkins73 avatar Jan 06 '22 17:01 davidwatkins73