Private Docs

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 + real superapp-dev ASB + cluster DB แล้ว verify state ใน DEV_OrchestratorDb ทุก hop หลักฐานละเอียด + refNo จริง: Atlas docs/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 เข้า WFfix 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-IdWfTemplate.Id ใน model ไม่มี migration → WF create 500 (42703)✅ fix แล้วใน development (DeleteUnUseGuid) — merge มาใช้ได้
F6FlowAccessGuard คืน Forbidden เป็น 500 (ไม่ใช่ 403)🔴 ทีม US · ไม่โดนถ้าเดิน OTP จริง (owner bind → guard ผ่าน)
maker-rejectmaker REJECT ไม่ publish workflow-decision → saga ค้าง UnderReview🔴 ต้อง reject ที่ approver
rework-endpointREWORK ต้องใช้ /rework · /action REWORK = lock step ไม่ publishℹ️ ใช้ endpoint ให้ถูก
cancel-endpoint/force-stop/cancel · force-stop ไม่ publish CANCELℹ️ ใช้ /cancel
resubmit-409ReworkHandler ตั้ง IsEditable=false / ResubmitHandler ต้องการ true → WF /resubmit 409 หลัง rework🔴 ทีม WF · OC→WF resubmit hop ติด (saga ฝั่ง OC ผ่านแล้ว)
company-lockjuristicId ที่ 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 ใหม่ถึงทดสอบได้