Journey Workflow
Multi-service orchestration — how journeys auto-generate workflows, create child applications, and propagate state via Pulsar.
A journey is a parent service that orchestrates multiple child services in a fixed sequence. The workflow is auto-generated from the journey mapping — you do not write transitions manually.
Flow overview
Journey mapping (JourneyMappingDto)
Saved via PUT /change-requests/{changeRequestId}/journey-mapping.
{
"serviceSequence": [
{
"order": 1,
"serviceCode": "BIRTH_CERT",
"serviceName": "Birth Certificate",
"changeRequestId": "<uuid>",
"required": true,
"serviceSource": "IREMBO_3_0",
"attachmentsMapping": null,
"repeatingFieldsMapping": null,
"stateMapping": {
"approve": "BIRTH_CERTIFICATE_APPROVED",
"reject": "BIRTH_CERTIFICATE_REJECTED"
}
},
{
"order": 2,
"serviceCode": "PASSPORT",
"serviceName": "Passport",
"changeRequestId": "<uuid>",
"required": false,
"serviceSource": "IREMBO_3_0",
"attachmentsMapping": null,
"repeatingFieldsMapping": null,
"stateMapping": { "approve": "PASSPORT_APPROVED", "reject": "PASSPORT_REJECTED" }
}
],
"triggerParentMappings": [
{
"childServiceCode": "BIRTH_CERT",
"childState": "BIRTH_CERTIFICATE_APPROVED",
"parentEvent": "APPROVE"
},
{
"childServiceCode": "BIRTH_CERT",
"childState": "BIRTH_CERTIFICATE_REJECTED",
"parentEvent": "REJECT"
},
{ "childServiceCode": "PASSPORT", "childState": "PASSPORT_APPROVED", "parentEvent": "APPROVE" },
{ "childServiceCode": "PASSPORT", "childState": "PASSPORT_REJECTED", "parentEvent": "APPROVE" }
],
"additionalApplicationSettings": {
"hideFromUserList": true,
"rfaOnParent": false
}
}Auto-generated states
JourneyWorkflowGeneratorUtil produces these states for each service in the sequence:
| State | Color | Notes |
|---|---|---|
NEW | primary | Fixed initial state |
SERVICE_CREATION_PENDING | yellow | Fixed second state |
<SERVICE_NAME>_PROCESSING | yellow | One per service |
<SERVICE_NAME>_APPROVED | success | One per service |
<SERVICE_NAME>_REJECTED | danger | One per service |
<SERVICE_NAME>_NOT_APPLICABLE | primary | Optional services only |
<JOURNEY_NAME>_COMPLETED | success | Final success state |
<JOURNEY_NAME>_REJECTED | danger | Final failure state (required service rejected) |
State names are derived from serviceName — uppercased with all non-word characters replaced by _.
Required vs optional services
| Scenario | Result |
|---|---|
| Required service approved | Continues to next service |
| Required service rejected | Journey moves to terminal <JOURNEY_NAME>_REJECTED |
| Optional service approved | Continues to next service |
| Optional service rejected | Continues to next service |
Optional service skipped (DO_YOU_NEED_<SERVICE_CODE> == "No") | <SERVICE_NAME>_NOT_APPLICABLE → continues |
APPLY_NORMAL_SERVICE breaking action
When the parent enters <SERVICE_NAME>_PROCESSING, this breaking action fires and creates the child application automatically:
{
"actionType": "APPLY_NORMAL_SERVICE",
"args": { "serviceCode": "BIRTH_CERT" }
}The child is created with:
creatorType: SYSTEM,applicationChannel: WEBparentApplicationIdset to the parent's UUID- Form data copied from
parentFormModel["BIRTH_CERT"]— the parent form must have the child's data nested under theserviceCodekey - These values injected into
applicationFormState:
{
"PARENT_APPLICATION_ID": "<parent UUID>",
"PARENT_APPLICATION_NUMBER": "<parent app number>",
"SERVICE_CODE": "BIRTH_CERT",
"APPLICANT_TYPE": "...",
"APPLICATION_STATE": "NEW",
"LOCALE": "en-US"
}Parent-child trigger flow (Pulsar)
After every state transition of a child that has a parentApplicationId, the application engine sends a Pulsar message. The consumer (PulsarTriggerParentConsumer) then:
- Fetches the parent's journey mapping from service-mgmt-engine
- Finds the matching entry in
triggerParentMappingsforchildServiceCode + childState - If no match — acknowledges and does nothing
- If match — merges child data into parent and triggers
workflowService.processApplication(parentEvent)
Trigger timing: fires after the breaking action and state persist, but before non-breaking actions (notifications) run.
Data merging
When child data is merged into the parent:
| Data | Merge strategy |
|---|---|
formModel | Child's form nested under formModel["SERVICE_CODE"] |
applicationDetails | Deduplicated by key — child overwrites parent on conflict |
userAttachments | Deduplicated by attachmentName — child overwrites on conflict |
certificates | Appended (no deduplication) |
additionalApplicationSettings
Passed directly from the journey mapping to each child application at creation time.
| Key | Type | Effect |
|---|---|---|
hideFromUserList | boolean | Hides child from the applicant's application list |
rfaOnParent | boolean | Restricts RFA — child cannot initiate its own RFA |
attachmentsMapping on serviceSequence
Maps officer attachments or certificates from one child service into another:
{
"officerAttachment": [
{
"targetField": "BIRTH_CERT_SCAN",
"sourceService": "BIRTH_CERT",
"attachmentKey": "CERTIFICATE_SCAN"
}
],
"certificates": [
{
"language": "en",
"targetField": "BIRTH_CERT_EN",
"sourceService": "BIRTH_CERT",
"certificateCode": "BIRTH_CERT_EN_CERT"
}
]
}serviceSource values
| Value | Meaning |
|---|---|
IREMBO_2_0 | Child service is on iRembo 2.0 |
IREMBO_2_5 | Child service is on iRembo 2.5 |
IREMBO_3_0 | Child service is on iRembo 3.0 (current) |
API endpoints
| Method | Path | Description |
|---|---|---|
GET | /change-requests/{changeRequestId}/journey-mapping | Fetch current journey mapping |
PUT | /change-requests/{changeRequestId}/journey-mapping | Save mapping — triggers workflow + form auto-generation |