Private Docs

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 เดิมผ่านเหมือนเดิม
3FE อ่าน field ใหม่แบบ optional — ไม่มี field = ไม่แสดงอะไร ไม่ errorรัน FE ใหม่กับ BE เก่า (ยังไม่มี field) แล้วหน้าจอต้องปกติ
4APIM 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 ไหนขึ้นกับใคร
1dev อีกคน (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 ของตัวเอง ตามกติกาของทีม:

  1. git checkout MIGRATION → port เฉพาะ source ของ data tier (entity / enum / EF config) จาก feature มา
  2. dotnet ef migrations add <ชื่อ> บน MIGRATION — ห้าม cherry-pick migration + snapshot จาก feature มา (lineage เพี้ยน)
  3. verify dotnet ef migrations has-pending-model-changes = none
  4. merge MIGRATION กลับเข้า feature branch ด้วย — feature ต้องพา migration ติดไป
  5. ห้าม merge developmentMIGRATION (จะลาก business logic เข้ามา)

คอลัมน์ใหม่ทุกตัวต้อง NULL ได้ — ไม่มีข้อยกเว้น

ทำไมผลถ้าไม่ทำ
ตอน migration รัน ข้อมูลเก่าที่มีอยู่ยังไม่มีค่าในคอลัมน์นั้นNOT NULL โดยไม่มี default = migration ล้มทันทีบนตารางที่มีข้อมูล
แอปเวอร์ชันเก่าที่ยังรันอยู่ไม่รู้จักคอลัมน์ใหม่ถ้าคอลัมน์บังคับค่า insert จากเวอร์ชันเก่าจะพังทันที
deploy ไม่ได้เกิดพร้อมกันทุก podมีช่วงที่เวอร์ชันเก่ากับใหม่รันพร้อมกันเสมอ

ค่า NULL ต้องแปลว่า “ยังไม่ได้ตรวจ / พฤติกรรมเดิม” เสมอ ห้ามแปลว่า “ผ่าน”


4. Phase 2 — Backend

รายละเอียดกลไกอยู่ที่ Architecture §4–§8 หน้านี้บอกแค่ลำดับงานและเงื่อนไขรับงาน

4.1 ลำดับงาน

#งานrepoหมายเหตุ
1API อ่าน screened list แบบ bulk ต่อ generation — คืน snapshotHeaderId + generationNo + evidenceFingerprint มาด้วยBackend_Centralizedของเดิมถาม custCode ทีละตัวและไม่บอกว่ามาจากรอบไหน จึงเขียน audit ที่อ้าง generation ไม่ได้ · ห้ามแก้ endpoint เดิม เพิ่มเส้นใหม่
2เพิ่ม field AMLO ใน UserInfoForRedis แบบ optional และ ห้าม bump CurrentSchemaVersionBackend_Packageguard ปัจจุบันใช้ strict != แล้วทิ้ง blob ทั้งก้อนถ้า version ไม่ตรง — bump = ทุก service ที่ยังไม่อัปเกรดพัง
3คอลัมน์ AMLO บน Companies (AmloStatus, AmloAsOfUtc, AmloGenerationId, AmloEvidenceFingerprint) — allow NULL ทุกตัวBackend_UserServiceต้องเพิ่มใหม่ เพราะ วันนี้ไม่มีคอลัมน์ flag เลย มีแต่ Status ที่ห้ามเอามาใช้ซ้ำ · ผ่าน branch MIGRATION
4ตาราง AmloUserUnblock + AmloAuditLogBackend_UserServiceaudit ต้องเขียนใน transaction เดียวกับการเปลี่ยนสถานะ · ผ่าน branch MIGRATION
5reconcile — อ่าน screened list → คำนวณ effective status ต่อ (user × company) → เขียน DB → refresh Redis → publish eventBackend_UserServiceลำดับ commit → invalidate ห้ามสลับ
6command ปลดราย user + กฎ revoke ตาม evidence fingerprintBackend_UserService
7guard ฝั่ง server รับ userId ไม่ใช่รับแค่ companyIdBackend_UserServiceD9 — user ที่ถูกปลดต้องสลับเข้าบริษัทที่ติดได้
8เปิด endpoint upload/download ทั่วไป + category หลักฐาน AMLO + magic byte ของ .xls/.docBackend_FileManagementServiceendpoint ที่ต้องใช้ ยัง comment ทิ้งอยู่ทั้งบล็อก ต้องเปิดและ hardening ใหม่
9endpoint sync มือ ต้องแยกผลลัพธ์ได้และสั่ง recompute เสมอBackend_Centralizedวันนี้ตอบ 200 เหมือนกันทั้งตอนสำเร็จ ไม่มีไฟล์ใหม่ และชนกับรอบอื่น
10ติด [RequireApiKey] ที่ AmloScreeningControllerBackend_Centralizedendpoint อ่าน 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 หลัง merge MIGRATION เข้า 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_HostAppSuperAppAmloBlockingCoordinator ที่ 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 ใหม่ทุก envamlo-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 ครึ่ง performancePhase 4 go-liveวัดได้หลัง implement เท่านั้น
virus scan ของไฟล์หลักฐานPhase 2 ข้อ 8ปัจจุบันปิดอยู่ ยังไม่ตัดสิน
เข้ารหัสคอลัมน์ชื่อใน snapshot_detailPhase 1ยังไม่ตัดสิน