AMLO Screening — Implementation Plan (4 Phases)
แผนลงมือทำแบ่ง 4 เฟส — Phase 1 ฝั่ง data tier ส่งต่อ dev อีกคนทำ parallel · Phase 2 BE · Phase 3 FE · Phase 4 Infrastructure พร้อมกติกาห้ามกระทบการใช้งานปัจจุบัน
อัปเดต: 2026-08-20
หน้านี้คือแผนลงมือทำ ไม่ใช่ design — เหตุผลเชิงสถาปัตยกรรมอยู่ที่ Architecture & Scenarios · Phase 1 มีเอกสารแยกสำหรับส่งต่อที่ Phase 1 Handoff
1. กติกาข้อเดียวที่อยู่เหนือทุกเฟส
| เฟส | ”ไม่กระทบ” แปลว่าอะไรในเฟสนี้ | ตรวจยังไง |
|---|---|---|
| 1 | ตารางใหม่ + คอลัมน์ใหม่เท่านั้น · ห้ามแก้พฤติกรรมการเขียนของ ingestion เดิมจนของที่มีอยู่เปลี่ยนความหมาย · endpoint เดิมยังตอบเหมือนเดิม | เรียก GET /v1/amlo-screening/{custCode} ก่อนและหลัง แล้ว response ต้องเหมือนเดิม · รอบ ingestion เดิมยังจบด้วยผลเท่าเดิม |
| 2 | คอลัมน์ใหม่ทุกตัว allow NULL · ค่า NULL = พฤติกรรมเดิมทุกกรณี · โค้ด enforcement ลงได้แต่ ยังไม่เปิดใช้ (dormant behind config) | รัน app เวอร์ชันเก่าชี้ DB ใหม่ต้องไม่พัง · เปิด feature flag = ปิด → ทุก flow เดิมผ่านเหมือนเดิม |
| 3 | FE อ่าน field ใหม่แบบ optional — ไม่มี field = ไม่แสดงอะไร ไม่ error | รัน FE ใหม่กับ BE เก่า (ยังไม่มี field) แล้วหน้าจอต้องปกติ |
| 4 | APIM policy เป็นลำดับสุดท้าย ตามคำสั่ง Owner (D1) เพราะกระทบ UAT ที่ process อยู่ | ต้องมีแผน rollback กลับ v6 ที่ทดสอบแล้วก่อนแตะ |
สิ่งเดียวที่ Business ตั้งใจให้กระทบ คือ ผู้ใช้ที่บริษัทติด AMLO จะทำรายการไม่ได้ — นอกจากนี้ไม่มีอะไรควรเปลี่ยนสำหรับใครเลย
2. ภาพรวม 4 เฟส
flowchart LR
P1["Phase 1 — Data tier<br/>ตารางใหม่ + จัดกลุ่มข้อมูล<br/>dev อีกคน ทำคู่ขนาน"]
P2["Phase 2 — Backend<br/>อ่าน source of truth<br/>บล็อก ปลด audit หลักฐาน"]
P3["Phase 3 — Frontend<br/>ShareLib HostApp AdminPortal"]
P4["Phase 4 — Infrastructure<br/>APIM facade v7"]
CONTRACT{{"contract ที่ freeze ไว้<br/>= schema ของตารางใหม่"}}
P1 -.ผลิต.-> CONTRACT
CONTRACT -.บริโภค.-> P2
P2 --> P3
P3 --> P4
| เฟส | ใครทำ | แตะ repo ไหน | ขึ้นกับใคร |
|---|---|---|---|
| 1 | dev อีกคน (handoff) | Backend_Centralized | ไม่ขึ้นกับใคร เริ่มได้ทันที |
| 2 | ทีมเรา | Backend_UserService · Backend_Package · Backend_Centralized (เฉพาะ API อ่าน) · Backend_FileManagementService | ต้องมี schema ที่ freeze แล้วจาก Phase 1 · ไม่ต้องรอ Phase 1 เขียนเสร็จ |
| 3 | ทีมเรา | Frontend_SuperAppLibraryUi · Frontend_HostAppSuperApp · Frontend_AdminSuperApp | ต้องมี API contract จาก Phase 2 |
| 4 | ทีมเรา | Backend_Iac (APIM policy) | ทำท้ายสุดเสมอ |
3. ขั้นตอนที่ทุกเฟสต้องทำเหมือนกัน
เริ่มงานทุกครั้ง:
git checkout development
git pull # ต้อง pull จริง — checkout ในเครื่องตามหลัง origin อยู่หลาย commit
git checkout -b feature/amlo-<ชื่องาน>
🔴 checkout ในเครื่องของหลาย repo ตามหลัง origin อยู่จริง (เคยทำให้สรุปผิดมาแล้ว 3 ครั้งในรอบก่อน) — ก่อนสรุปว่าโค้ดเป็นยังไง ให้ดู git show origin/development:<path> เสมอ ไม่ใช่ดู working tree
ทุกงานที่แตะ schema (entity / EF config / migration / ModelSnapshot):
ต้องทำบน branch MIGRATION ไม่ใช่ feature branch ของตัวเอง ตามกติกาของทีม:
git checkout MIGRATION→ port เฉพาะ source ของ data tier (entity / enum / EF config) จาก feature มาdotnet ef migrations add <ชื่อ>บนMIGRATION— ห้าม cherry-pick migration + snapshot จาก feature มา (lineage เพี้ยน)- verify
dotnet ef migrations has-pending-model-changes= none - merge
MIGRATIONกลับเข้า feature branch ด้วย — feature ต้องพา migration ติดไป - ห้าม merge
development→MIGRATION(จะลาก business logic เข้ามา)
คอลัมน์ใหม่ทุกตัวต้อง NULL ได้ — ไม่มีข้อยกเว้น
| ทำไม | ผลถ้าไม่ทำ |
|---|---|
| ตอน migration รัน ข้อมูลเก่าที่มีอยู่ยังไม่มีค่าในคอลัมน์นั้น | NOT NULL โดยไม่มี default = migration ล้มทันทีบนตารางที่มีข้อมูล |
| แอปเวอร์ชันเก่าที่ยังรันอยู่ไม่รู้จักคอลัมน์ใหม่ | ถ้าคอลัมน์บังคับค่า insert จากเวอร์ชันเก่าจะพังทันที |
| deploy ไม่ได้เกิดพร้อมกันทุก pod | มีช่วงที่เวอร์ชันเก่ากับใหม่รันพร้อมกันเสมอ |
ค่า NULL ต้องแปลว่า “ยังไม่ได้ตรวจ / พฤติกรรมเดิม” เสมอ ห้ามแปลว่า “ผ่าน”
4. Phase 2 — Backend
รายละเอียดกลไกอยู่ที่ Architecture §4–§8 หน้านี้บอกแค่ลำดับงานและเงื่อนไขรับงาน
4.1 ลำดับงาน
| # | งาน | repo | หมายเหตุ |
|---|---|---|---|
| 1 | API อ่าน screened list แบบ bulk ต่อ generation — คืน snapshotHeaderId + generationNo + evidenceFingerprint มาด้วย | Backend_Centralized | ของเดิมถาม custCode ทีละตัวและไม่บอกว่ามาจากรอบไหน จึงเขียน audit ที่อ้าง generation ไม่ได้ · ห้ามแก้ endpoint เดิม เพิ่มเส้นใหม่ |
| 2 | เพิ่ม field AMLO ใน UserInfoForRedis แบบ optional และ ห้าม bump CurrentSchemaVersion | Backend_Package | guard ปัจจุบันใช้ strict != แล้วทิ้ง blob ทั้งก้อนถ้า version ไม่ตรง — bump = ทุก service ที่ยังไม่อัปเกรดพัง |
| 3 | คอลัมน์ AMLO บน Companies (AmloStatus, AmloAsOfUtc, AmloGenerationId, AmloEvidenceFingerprint) — allow NULL ทุกตัว | Backend_UserService | ต้องเพิ่มใหม่ เพราะ วันนี้ไม่มีคอลัมน์ flag เลย มีแต่ Status ที่ห้ามเอามาใช้ซ้ำ · ผ่าน branch MIGRATION |
| 4 | ตาราง AmloUserUnblock + AmloAuditLog | Backend_UserService | audit ต้องเขียนใน transaction เดียวกับการเปลี่ยนสถานะ · ผ่าน branch MIGRATION |
| 5 | reconcile — อ่าน screened list → คำนวณ effective status ต่อ (user × company) → เขียน DB → refresh Redis → publish event | Backend_UserService | ลำดับ commit → invalidate ห้ามสลับ |
| 6 | command ปลดราย user + กฎ revoke ตาม evidence fingerprint | Backend_UserService | |
| 7 | guard ฝั่ง server รับ userId ไม่ใช่รับแค่ companyId | Backend_UserService | D9 — user ที่ถูกปลดต้องสลับเข้าบริษัทที่ติดได้ |
| 8 | เปิด endpoint upload/download ทั่วไป + category หลักฐาน AMLO + magic byte ของ .xls/.doc | Backend_FileManagementService | endpoint ที่ต้องใช้ ยัง comment ทิ้งอยู่ทั้งบล็อก ต้องเปิดและ hardening ใหม่ |
| 9 | endpoint sync มือ ต้องแยกผลลัพธ์ได้และสั่ง recompute เสมอ | Backend_Centralized | วันนี้ตอบ 200 เหมือนกันทั้งตอนสำเร็จ ไม่มีไฟล์ใหม่ และชนกับรอบอื่น |
| 10 | ติด [RequireApiKey] ที่ AmloScreeningController | Backend_Centralized | endpoint อ่าน watchlist ที่ยังไม่มี auth เลย · confirm ว่าไม่มี caller ภายนอกเรียกอยู่ก่อนติด (controller ข้างๆ ติดไว้แล้ว = เป็นการตัดสินใจที่แยกกันไว้ตั้งใจ ไม่ใช่ลืม) · แก้ได้แยก PR เดี๋ยวนี้ |
4.2 เงื่อนไขรับงาน Phase 2
- คอลัมน์ใหม่ทุกตัว
NULLได้ และรันแอปเวอร์ชันก่อนหน้าชี้ DB ใหม่แล้วไม่พัง -
CurrentSchemaVersionของUserInfoForRedisไม่ถูก bump และ blob เก่า deserialize ได้ - endpoint เดิมทุกเส้นตอบเหมือนเดิม (มี test เทียบ response ก่อน/หลัง)
- enforcement ยังปิดอยู่ — เปิด flag = ปิด แล้วทุก flow เดิมผ่าน
-
has-pending-model-changes= none หลัง mergeMIGRATIONเข้า feature - มี test ของ Redis refresh ล้มเหลว — วันนี้
CacheRefreshBehaviorกลืน exception แล้วตอบ200ซึ่งกลายเป็นช่องโหว่ทันทีที่ Redis กลายเป็นตัวบอกสถานะบล็อก
5. Phase 3 — Frontend
ทำแบบเดียวกับ Phase 2 (checkout development → pull → branch ใหม่) แต่ฝั่งหน้าจอ
| repo | ทำอะไร |
|---|---|
Frontend_SuperAppLibraryUi (ShareLib) | component/type ที่ใช้ร่วมกัน · ถ้าแตะ @exim/ui-kit ต้องเช็คว่า ex-modal ส่ง data-testid ทะลุถึง host element จริงไหม (ยังไม่ verify) |
Frontend_HostAppSuperApp | AmloBlockingCoordinator ที่ authenticated layout · modal 5 สถานะ · interceptor อ่าน errorCode · subscribe AmloStateChanged |
Frontend_AdminSuperApp | เมนู AMLO Management · หน้า Operations · หน้า Compliance Cases (รายชื่อสมาชิก + เลือกปลดหลายคน + อัปโหลดหลักฐาน) · หน้า Test Simulation เฉพาะ non-production |
เงื่อนไขรับงาน Phase 3
- FE ใหม่ + BE เก่า (ยังไม่มี field AMLO) = หน้าจอปกติ ไม่มี error ไม่มี modal ค้าง
- modal ปิดได้เมื่อ
/users/meยืนยันเท่านั้น ห้ามปิดจาก local state - Admin Portal มี guard ต่อ child route แยกกัน — วันนี้
menuAccessGuardมีอยู่แต่canActivateChildยัง comment ไว้ - Production artifact ต้องไม่มี Test Simulation ทั้ง route/chunk/menu — และต้องเช็ค permission/menu dataset ฝั่ง backend แยกต่างหาก เพราะเมนูมาจาก dataset ไม่ได้มาจาก artifact
- อัปโหลดทีละไฟล์ผูกด้วย correlation id ไม่ใช่ยิง 10 ไฟล์ใน request เดียว (10 × 10 MB เกินเพดาน request กลาง 10 MB)
6. Phase 4 — Infrastructure
ทำหลังสุดเสมอ ตามที่ Owner สั่งไว้ใน D1 เพราะกระทบ UAT ที่กำลัง process อยู่
| งาน | หมายเหตุ |
|---|---|
| APIM facade v6 → v7 | เปลี่ยนไปใช้ envelope เดียว sess:v7:{digest} · ต้องใช้ key prefix ใหม่ เพื่อให้ rollout/rollback เป็น cache miss ไม่ใช่ตีความ JSON เป็น bearer token |
| Named Value ใหม่ทุก env | amlo-max-age-seconds, amlo-unknown-ttl-seconds, session-cache-type |
DailyRunHourBangkok = 5 | วันนี้เป็น 23 ทุก env และ sit/uat ยัง Enabled=false |
เงื่อนไขรับงาน Phase 4
- มีแผน rollback กลับ v6 ที่ ทดสอบแล้วจริง ไม่ใช่เขียนไว้เฉยๆ
- steady-state
Unknown= 0 ก่อนเปิด enforcement - ทดสอบ rolling upgrade ว่า envelope รุ่นเก่าไม่ถูกตีความเป็น
Clear - load test เส้น
resolve-sessionตอน cache miss ผ่านก่อน go-live
7. สิ่งที่ยังไม่มีคำตอบและกระทบแผน
| เรื่อง | กระทบเฟสไหน | สถานะ |
|---|---|---|
| dedup key ของการจัดกลุ่ม — จัดกลุ่มยังไงได้ผลต่างกัน 96 / 107 / 109 / 122 แถวจากข้อมูลชุดเดียวกัน | Phase 1 (และ Phase 2 ที่อ่านต่อ) | ❓ Compliance ต้องเป็นคนตัดสิน ดู Phase 1 Handoff |
| D7 ไฟล์เป็น full snapshot หรือ delta | เปิด auto-unblock (ไม่บล็อกการเขียนโค้ด) | รอไฟล์เข้ามาอีกอย่างน้อย 1 รอบ |
amlo-max-age-seconds ครึ่ง performance | Phase 4 go-live | วัดได้หลัง implement เท่านั้น |
| virus scan ของไฟล์หลักฐาน | Phase 2 ข้อ 8 | ปัจจุบันปิดอยู่ ยังไม่ตัดสิน |
เข้ารหัสคอลัมน์ชื่อใน snapshot_detail | Phase 1 | ยังไม่ตัดสิน |