E2E Real-Run Sequence Diagrams — ขับจริงทุก flow
Sequence diagram ของ E2E onboarding/approval ที่ขับจริงบน local fleet (real ASB + cluster DB) ครบทุก flow: Approve / Reject / Rework / Resubmit / Cancel + by-document re-resolve — พร้อมจุด mock และ bug ที่เจอ
อัปเดต: 2026-06-25
เอกสารนี้ = ภาพ sequence ของ “รันจริง” (คู่กับ
06-e2e-runtime-verificationที่เป็นสรุปผล/หลักฐาน) ทุก diagram ด้านล่าง ขับจริงผ่าน HTTP บน local fleet 7 service + realsuperapp-devASB + cluster DB แล้ว verify state ใน DEV_OrchestratorDb ทุก hop หลักฐานละเอียด + refNo จริง: Atlasdocs/orchestration-integration/25062026/REAL_E2E_RUN_RESULT.md
สิ่งที่ต้องตั้งก่อน (setup ที่ทำให้ flow เดินได้ local)
| # | ของชั่วคราว | ทำไม |
|---|---|---|
| 🔧 | mock Entra/OTP ใน US (LOCAL_E2E_MOCK_OTP=true) | OTP onboarding เป็นของ Microsoft Entra (external) → email มั่ว/อ่าน Redis ไม่ได้ local |
| 🔑 | owner Bearer JWT (ตอน resubmit) | หลัง rework cookie ถูกล้าง + ทำ OTP ซ้ำไม่ได้ → ใช้ JWT (nameid==OwnerUserId) ที่ env Local รับ token ไม่ signed |
| 🔀 | merge development เข้า WF | fix bug WfTemplate.Id (WF create เคย 500) |
| ⏸️ | pause cluster OC (ตอน happy path) | กัน cluster OC แย่ง message จาก local OC |
สัญลักษณ์ใน diagram: 🔧 = จุด mock · ✅ = verify DB แล้ว · 🔴 = bug ที่เจอ · 🟡 = raised แต่ยังไม่ verify
ภาพรวม — OC saga state machine
stateDiagram-v2
[*] --> AwaitingPickup: flow-submitted (US)
AwaitingPickup --> UnderReview: workflow-pickup (WF maker)
UnderReview --> UnderConsideration: sent-to-reviewer (WF maker APPROVE)
UnderConsideration --> InApprove: approval-pickup (WF approver)
InApprove --> Approved: workflow-decision APPROVE
UnderReview --> Rejected: workflow-decision REJECT
UnderConsideration --> Rejected: workflow-decision REJECT
InApprove --> Rejected: workflow-decision REJECT
UnderReview --> ReworkRequested: workflow-decision REWORK
ReworkRequested --> UnderReview: flow-submitted isResubmit
AwaitingPickup --> Cancelled: workflow-decision CANCEL
UnderReview --> Cancelled: workflow-decision CANCEL
InApprove --> Cancelled: workflow-decision CANCEL
Approved --> [*]
Rejected --> [*]
Cancelled --> [*]
A — Happy Path (Approve) · saga ✅ Approved/Completed
ขับเต็มเส้น: US OTP→submit → OC saga → WF maker review → WF approver decision → Approved · refNo จริง FX-202606-9643
sequenceDiagram
autonumber
actor R as Requestor (HTTP)
participant US as UserService
participant ASB as ASB superapp-dev
participant OC as Orchestrator
participant DB as DEV_OrchestratorDb
participant WF as WorkflowService
participant TS as TaskService
Note over US: 🔧 LOCAL_E2E_MOCK_OTP — Entra/OTP/Graph stubbed (fake email OK)
rect rgb(232,248,232)
Note over R,US: PART 1 — US onboarding walk (OTP step 1 → submit)
R->>US: POST /onboarding/instances {flowCode:NEW_REQUESTOR, email:fake}
US-->>R: 200 · currentStep=OtpVerificationStep
R->>US: POST OtpVerificationStep/SaveDraft {email}
US->>US: 🔧 stub SignUpStart (skip real Entra) · ออก refNo
US-->>R: 200 · OTP_SENT · refNo=FX-202606-9643
R->>US: POST OtpVerificationStep/Next {otp:000000}
US->>US: 🔧 stub verify → createUser + BindOwner + rotate EditSession
US-->>R: 200 VERIFIED · Set-Cookie __Host-onboarding
R->>US: POST Next × Juristic→UserMode→Designate→CompanyInfo→Signatory (carry cookie)
US-->>R: 200 (ทุก step)
R->>US: POST FxRequestorTcConsentStep/Submit {agreed:true}
US->>ASB: publish flow-submitted-for-approval {RefNo, FlowInstanceId, isResubmit:false}
US-->>R: 200 · status=Submitted
end
rect rgb(232,248,232)
Note over ASB,WF: PART 2 — OC consume → saga create → WF create
ASB->>OC: flow-submitted-for-approval
OC->>DB: INSERT saga = AwaitingPickup ✅ + correlations(RefNo, FlowInstanceId)
OC->>WF: POST /Workflow/instances (create, via outbox)
WF-->>OC: 200 · WfInstance #29 (DocStatus=Submitted)
end
rect rgb(232,248,232)
Note over R,DB: PART 3 — WF maker review (2 hop)
R->>WF: PATCH /pickup {actionBy:maker}
WF->>TS: assign task (InReview)
WF->>ASB: publish workflow-pickup
ASB->>OC: workflow-pickup
OC->>DB: saga → UnderReview ✅
R->>WF: POST /{id}/action APPROVE (maker · non-final)
WF->>ASB: publish sent-to-reviewer
ASB->>OC: sent-to-reviewer
OC->>DB: saga → UnderConsideration ✅
end
rect rgb(232,248,232)
Note over R,DB: PART 4 — WF approver decision (2 hop)
R->>WF: PATCH /pickup {actionBy:approver}
WF->>ASB: publish approval-pickup
ASB->>OC: approval-pickup
OC->>DB: saga → InApprove ✅
R->>WF: POST /{id}/action APPROVE (approver · final)
WF->>ASB: publish workflow-decision {APPROVE}
ASB->>OC: workflow-decision
OC->>DB: saga → Approved · Lifecycle=Completed ✅
end
rect rgb(255,243,224)
Note over OC,US: PART 5 — return path (🟡 raised, ยังไม่ verify รอบนี้)
OC->>ASB: flow-progress-update / flow-approval-result (outbox)
ASB-->>US: US consume → flip FlowInstance (ไม่ได้ตรวจ)
end
attribution: local OC เป็นคน consume 4 saga hop จริง (orchestrator.log: ProcessedMessages ×4 + UPDATE ×4, 0 conflict) · WfInstance #29 ตอน submit คลัสเตอร์ WF เป็นคนสร้าง (ก่อน merge fix) — หลัง merge local WF create ได้แล้ว
B — Reject · saga ✅ Rejected
reject ที่ approver stage (terminal decision) · refNo FX-202606-9876
sequenceDiagram
autonumber
actor R as Requestor
participant WF as WorkflowService
participant ASB as ASB
participant OC as Orchestrator
participant DB as DEV_OrchestratorDb
Note over R,DB: เริ่มจาก InApprove (pickup maker → APPROVE maker → pickup approver)
R->>WF: POST /{id}/action REJECT (approver)
WF->>ASB: publish workflow-decision {REJECT}
ASB->>OC: workflow-decision
OC->>DB: saga OnDecision.Reject (ExpectAnyState UnderReview/UnderConsideration/InApprove) → Rejected ✅
Note over R,WF: 🔴 maker-stage REJECT ไม่ publish workflow-decision<br/>(WF ตั้ง DocStatus=REJECTED แต่ไม่ส่งให้ OC) → saga ค้าง UnderReview<br/>→ ต้อง reject ที่ approver เท่านั้น
C — Rework → Resubmit · saga ✅ ReworkRequested → UnderReview · + G (by-document)
rework + ยื่นใหม่ครบวง · refNo FX-202606-9979 · รวม G (OC re-resolve WF handle จาก refNo)
sequenceDiagram
autonumber
actor R as Requestor
participant US as UserService
participant WF as WorkflowService
participant ASB as ASB
participant OC as Orchestrator
participant DB as DEV_OrchestratorDb
rect rgb(232,248,232)
Note over R,DB: REWORK — เริ่มจาก UnderReview (หลัง pickup maker)
R->>WF: POST /{id}/rework {reworkSteps:[CompanyInfoStep]}
Note over WF: ใช้ /rework (dedicated) — /action REWORK ไม่ publish (lock step เฉยๆ)
WF->>ASB: publish workflow-decision {REWORK}
ASB->>OC: workflow-decision
OC->>DB: saga OnDecision.Rework → ReworkRequested ✅
OC->>ASB: flow-approval-result {CORRECTION_REQUESTED} (outbox)
ASB->>US: FlowApprovalResultConsumer → FlowInstance.Status=Rework
end
rect rgb(232,248,232)
Note over R,DB: RESUBMIT — 🔑 cookie ถูกล้างตอน submit, OTP ซ้ำได้ ALREADY_VERIFIED → ใช้ owner JWT
R->>US: POST /open [Bearer owner-JWT · nameid==OwnerUserId]
US-->>R: 200 · currentStep=CompanyInfoStep (rework target)
R->>US: POST CompanyInfoStep/Next (แก้ target) [Bearer]
R->>US: POST /Resubmit [Bearer]
US->>ASB: publish flow-submitted {isResubmit:true, FlowInstanceId เดิม}
ASB->>OC: flow-submitted (isResubmit)
OC->>DB: saga OnResubmit (ExpectState ReworkRequested) → UnderReview ✅
end
rect rgb(255,232,232)
Note over OC,WF: G — OC re-resolve WF handle จาก refNo (ไม่อ่าน cached WfInstanceId เลย → by-document คือทางเดียว)
OC->>WF: GET /instances/by-document/{refNo}
WF-->>OC: 200 · WfInstanceId=35 ✅ (re-resolve สำเร็จ = กลไก G)
OC->>WF: POST /Workflow/35/resubmit
WF-->>OC: 🔴 409 Conflict · IsEditable=false<br/>(ReworkHandler ตั้ง false / ResubmitHandler ต้องการ true) → WF ไม่ re-enter InReview
end
G reframe: ไม่มี branch “ถ้าลืม WfInstanceId ค่อย fallback” —
HttpWorkflowAdapter.Resubmitเรียกby-documentเสมอ → C’s resubmit พิสูจน์ G ไปแล้ว · การลบ correlation เพื่อจำลอง session-loss = no-op (code ไม่อ่าน cache)
D — Cancel · saga ✅ Cancelled
cancel ผ่าน /cancel (CancelHandler) · refNo FX-202606-3068
sequenceDiagram
autonumber
actor R as Requestor
participant WF as WorkflowService
participant ASB as ASB
participant OC as Orchestrator
participant DB as DEV_OrchestratorDb
Note over R,DB: เริ่มจาก UnderReview (หลัง pickup)
R->>WF: POST /{id}/cancel {taskId, userId, reason}
Note over WF: ใช้ /cancel — 🔴 /force-stop ≠ /cancel<br/>(force-stop force-stop step แต่ไม่ publish CANCEL)
WF->>WF: Status=CANCELLED · PATCH task=CANCEL
WF->>ASB: publish workflow-decision {CANCEL}
ASB->>OC: workflow-decision
OC->>DB: saga OnCancel (ไม่มี ExpectState — ทำได้ทุก state) → Cancelled ✅
สรุป bug/gap ที่เจอตอนขับจริง (deploy-relevant)
| # | finding | สถานะ |
|---|---|---|
| WF-Id | WfTemplate.Id ใน model ไม่มี migration → WF create 500 (42703) | ✅ fix แล้วใน development (DeleteUnUseGuid) — merge มาใช้ได้ |
| F6 | FlowAccessGuard คืน Forbidden เป็น 500 (ไม่ใช่ 403) | 🔴 ทีม US · ไม่โดนถ้าเดิน OTP จริง (owner bind → guard ผ่าน) |
| maker-reject | maker REJECT ไม่ publish workflow-decision → saga ค้าง UnderReview | 🔴 ต้อง reject ที่ approver |
| rework-endpoint | REWORK ต้องใช้ /rework · /action REWORK = lock step ไม่ publish | ℹ️ ใช้ endpoint ให้ถูก |
| cancel-endpoint | /force-stop ≠ /cancel · force-stop ไม่ publish CANCEL | ℹ️ ใช้ /cancel |
| resubmit-409 | ReworkHandler ตั้ง IsEditable=false / ResubmitHandler ต้องการ true → WF /resubmit 409 หลัง rework | 🔴 ทีม WF · OC→WF resubmit hop ติด (saga ฝั่ง OC ผ่านแล้ว) |
| company-lock | juristicId ที่ submit แล้วใช้ซ้ำไม่ได้ (Application.CompanyLocked) | ℹ️ ใช้ juristicId ใหม่ต่อ run |
ขอบเขต / ยังไม่ verify
- return path OC→US (
flow-progress-update/flow-approval-resultพลิก US FlowInstance) + NS แจ้ง requestor — ยังไม่ตรวจ (saga ฝั่ง OC ถึง terminal แล้วเท่านั้น) - E Customer auto-approve — คนละ flow (
CUSTOMER_FIXED_ONBOARDING, ไม่แตะ OC/WF) - F Multi-approver —
OnboardingFXเป็น single-approver (approver phase SEQUENTIAL 1 step) → ต้อง template 2-approver ใหม่ถึงทดสอบได้