Capstone 01 — The process model: application → decision → offer → disbursal
Motto — Nothing in this model is new; the capstone is the proof that the pieces compose.
Part of Phase 11 — Capstone. Combines Phases 1, 3, 4, 7.
The Project
One process —
outputs/loan-origination.bpmn20.xml —
carrying a loan application from arrival to disbursal, decline, or expiry:
<style> .dgm-cap{background:var(–color-surface);border:1px solid var(–color-border);border-top:3px solid var(–color-border-danger-outline); border-radius:16px;padding:28px 26px 22px;box-shadow:0 1px 3px rgb(var(–shadow-color) / 0.04),0 8px 24px rgb(var(–shadow-color) / 0.04); margin:26px 0;max-width:100%;font-family:inherit;text-align:left;position:relative} .dgm-cap-chip{position:absolute;top:-11px;left:24px;background:var(–color-background-danger-solid);color:var(–white); font-size:10.5px;font-weight:700;letter-spacing:1px;text-transform:uppercase; border-radius:20px;padding:3px 12px;box-shadow:0 2px 6px color-mix(in oklab, var(–color-background-danger-solid) 28%, transparent)} .dgm-cap h4{margin:0 0 4px;font-size:11px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(–color-text-secondary)} .dgm-cap-sub{font-size:12px;color:var(–color-text-secondary);margin:0 0 16px} .dgm-cap-chain{display:flex;flex-direction:column;gap:2px} .dgm-cap-node{background:var(–color-background-danger-surface);border:1.5px solid var(–color-border-danger-surface);border-radius:10px;padding:9px 14px; font-weight:700;font-size:11px;color:var(–color-text-danger)} .dgm-cap-node.alt{background:var(–color-surface-tertiary);border-color:var(–color-border);color:var(–color-text)} .dgm-cap-arr{display:flex;justify-content:center;color:var(–color-text-tertiary);padding:1px 0;font-size:9.5px;text-align:center} @media (max-width:640px){.dgm-cap{padding:20px 16px 18px}} .dgm-cap-q{background:var(–color-surface-tertiary);border:1px dashed var(–color-border);border-radius:10px;padding:9px 14px;text-align:center; font-size:11px;font-weight:700;color:var(–color-text);margin-bottom:10px} .dgm-cap-branches{display:grid;grid-template-columns:repeat(3,1fr);gap:10px} .dgm-cap-b{border-radius:11px;padding:11px 13px;border:1.5px solid} .dgm-cap-b h6{margin:0 0 4px;font-size:10.5px;font-weight:700} .dgm-cap-b p{margin:0;font-size:10px;color:var(–color-text-secondary);line-height:1.35} .dgm-cap-b.accent{background:var(–color-background-danger-surface);border-color:var(–color-border-danger-surface)} .dgm-cap-b.accent h6{color:var(–color-text-danger)} .dgm-cap-b.blue{background:var(–color-background-info-surface);border-color:var(–color-border-info-surface)} .dgm-cap-b.blue h6{color:var(–color-text-info)} .dgm-cap-b.green{background:var(–color-background-success-surface);border-color:var(–color-border-success-surface)} .dgm-cap-b.green h6{color:var(–color-text-success)} .dgm-cap-b.red{background:var(–color-background-danger-surface);border-color:var(–color-border-danger-surface)} .dgm-cap-b.red h6{color:var(–color-text-danger)} .dgm-cap-b.neutral{background:var(–color-surface-tertiary);border-color:var(–color-border)} .dgm-cap-b.neutral h6{color:var(–color-text)} @media (max-width:640px){.dgm-cap{padding:20px 16px 18px}.dgm-cap-branches{grid-template-columns:1fr}} </style> <div class=“dgm-cap”> <span class=“dgm-cap-chip”>Capstone: loan origination, end to end</span> <h4>One process, four decision points, three ways to end</h4> <p class=“dgm-cap-sub”>Bureau pull and KYC review run in parallel; everything else is sequential.</p> <div class=“dgm-cap-chain”> <div class=“dgm-cap-node alt”>● Start → fork</div> </div> <div class=“dgm-cap-q”>Parallel branches</div> <div class=“dgm-cap-branches” style=“grid-template-columns:repeat(2,1fr)”> <div class=“dgm-cap-b blue”> <h6>Bureau pull 🌐 (async)</h6> <p>⚡ error → manual bureau pull 👤</p> </div> <div class=“dgm-cap-b accent”> <h6>KYC review 👤</h6> <p>Runs alongside the bureau pull</p> </div> </div> <div class=“dgm-cap-chain”> <div class=“dgm-cap-node alt”>Join</div> </div> <div class=“dgm-cap-q”>KYC ok?</div> <div class=“dgm-cap-branches” style=“grid-template-columns:repeat(2,1fr)”> <div class=“dgm-cap-b red”> <h6>No → ● rejected</h6> </div> <div class=“dgm-cap-b green”> <h6>Yes → continue</h6> <p>Credit decision 📋 DMN</p> </div> </div> <div class=“dgm-cap-q”>Decision?</div> <div class=“dgm-cap-branches”> <div class=“dgm-cap-b green”> <h6>Auto-approve</h6> <p>→ Accept offer 👤 ⏰ P30D</p> </div> <div class=“dgm-cap-b accent”> <h6>Manual review</h6> <p>Credit review 👤 → approve or decline</p> </div> <div class=“dgm-cap-b red”> <h6>Decline</h6> <p>→ ● declined</p> </div> </div> <div class=“dgm-cap-q”>Offer outcome</div> <div class=“dgm-cap-branches” style=“grid-template-columns:repeat(2,1fr)”> <div class=“dgm-cap-b green”> <h6>Accepted</h6> <p>Disburse ⚙️ → ● disbursed</p> </div> <div class=“dgm-cap-b neutral”> <h6>⏰ Expired (P30D)</h6> <p>→ ● expired</p> </div> </div> </div>
Every element cites its lesson:
| Element | Built in |
|---|---|
| parallel fork/join over bureau + KYC | Phase 1, lesson 02 |
| async HTTP bureau task, failure → BPMN error → manual fallback | Phase 4, lessons 02/04; Phase 2, lesson 03 |
| candidate groups + form properties on every human task | Phase 3, lessons 02/04 |
flowable:type="dmn" decision task, gateway routing on ${decision} |
Phase 5, lesson 03 |
interrupting offer-expiry timer, duration from ${offerValidity} |
Phase 7, lesson 01 |
Three composition decisions worth defending in review:
- The bureau task is async (
flowable:async="true"). A third-party call must not hold the start transaction hostage, and retries come free (Phase 2’s rules). Its error path is a designed fallback, not an incident. - KYC gate before the decision task. There’s no point pricing a file that fails KYC. Ordering checks by cost and kill-probability is process design, not engine mechanics.
- Offer validity is a variable, not a literal
P30D. The value can migrate into the DMN table later without touching the model (Phase 5’s governance boundary).
Verify It
python3 - <<'PY'
import xml.dom.minidom; xml.dom.minidom.parse(
"flowable/phases/11-capstone-loan-origination/01-process-model/outputs/loan-origination.bpmn20.xml")
print("well-formed")
PY
Then deploy and run it — that’s lesson 03; the decision table it references is lesson 02.
Challenge. Add the two Phase 7 event subprocesses from
lesson 7.05
— interrupting customerWithdrawal, non-interrupting weekly nudge — and re-verify.
The diff should be purely additive: that’s the composition property working.
