9.12 · Scorecard + modernization target
บทปิดท้าย Volume 9 — สรุปสุขภาพของ data layer ในรูปแบบ scorecard + ทิศทาง modernization ที่ควรจะเป็น
Legend
- 🟢 เขียว — ผ่านเกณฑ์ ใช้ได้ดี
- 🟡 เหลือง — ใช้ได้ แต่มีจุดอ่อน ต้องระวังตอนแก้
- 🔴 แดง — มีปัญหาจริง ควร refactor
Scorecard สรุป
| Domain | 🟢 | 🟡 | 🔴 |
|---|---|---|---|
| Persistence topology | 1 | 2 | 2 |
| Model patterns | 2 | 3 | 2 |
| Schema management | 0 | 1 | 2 |
| Indexing | 1 | 1 | 1 |
| Transactions | 0 | 0 | 1 |
| Data integrity | 1 | 3 | 3 |
Scorecard ตาม dimension
| Dimension | สถานะ | หมายเหตุ |
|---|---|---|
| Connection pool sizing | 🔴 | max 100 × 2 conn × 4 replica = 800 (เกิน MariaDB default 151) |
| Multi-database topology | 🟡 | 4 logical DBs, cross-schema write ไม่มี transaction |
| Charset consistency | 🟡 | connector utf8mb4 / model ใช้ utf8 (mixed) |
| PK strategy | 🟢 | UUID ทุกที่ + UUIDV4 default |
| Money type | 🟢 | DECIMAL(12,2) ทุก field เงิน |
| AllowNull discipline | 🔴 | เกือบทุก field nullable → validation อยู่ที่ controller อย่างเดียว |
| ENUM discipline | 🟡 | บาง model ใช้ TEXT แทน ENUM (StudyLeaveStatus) |
| Migration tool | 🔴 | syncModelToTable + manual ALTER → ไม่มี audit / rollback |
| syncModels list | 🔴 | hardcoded array 80+ entries → dev ลืมเพิ่ม |
| Index coverage | 🟡 | 36/95 model มี index declaration (~38%) |
| Composite / unique index | 🟢 | มีบางที่ composite และ unique ที่ถูกต้อง |
| Transaction correctness | 🔴 | argument ที่ 3 ของ update ไม่ใช้ transaction → silent fail |
| Cross-schema relation | 🟡 | logical FK ทั้งหมด → orphan rows ได้ |
| PII encryption at rest | 🔴 | idcard, token, refreshToken plaintext |
| Soft-delete consistency | 🟡 | 3 patterns ผสม (isDeleted / isActive / status) |
| Identity canonical ID | 🔴 | 3+ PK strategies, ไม่มี canonical employee ID |
| JSON field validation | 🟡 | Users.role, WDExport.wdHospitalIds, WDHistory.payload |
| Duplicated column declarations | 🟡 | WDHospital.coaCodeName + isJuristic declare สองครั้ง |
| Type discipline (UUID vs STRING) | 🟡 | บางที่ UUID เป็น STRING (MasterApprover, FundRequest) |
| Session cleanup | 🟡 | expireDate มี แต่ไม่มี scheduled cleanup |
รายการ modernization target
| ปัจจุบัน | ควรจะเป็น | Priority |
|---|---|---|
syncModelToTable() auto-sync | Migration tool (sequelize-cli, umzug, golang-migrate) | สูง |
Hardcoded syncModels array | Dynamic discover จาก model registry | กลาง |
| Manual ALTER ผ่าน phpMyAdmin | Migration files ที่ review + commit ใน git | สูง |
transaction: t ผิดตำแหน่ง | Pattern update(args, { ...options, transaction: t }) | สูง |
| Logical FK ข้าม schema | FK constraint จริง / หรือ consolidate schema | กลาง |
| 3 PK strategies สำหรับ User | canonical employeeId UUID stable | สูง |
| PII plaintext | Column-level encryption หรือ MariaDB AES | สูง |
pool.max: 100 | ลดเหลือ 20-30 + monitor | กลาง |
| utf8/utf8mb4 mixed | utf8mb4 ทุกที่ | ต่ำ |
allowNull: true เกือบทุก field | NOT NULL + default ที่จำเป็น | กลาง |
| 3 patterns ของ soft-delete | เลือก pattern เดียว (isDeleted BOOLEAN) | กลาง |
| JSON-encoded role | Relational user_roles join table | สูง |
| Duplicated columns | Code review + linter ที่ตรวจจับ | ต่ำ |
STRING(36) UUID-as-string | Native UUID type | ต่ำ |
| Session expireDate ไม่ cleanup | Cron job cleanup + retention policy | กลาง |
ลำดับความสำคัญ — ควรทำอะไรก่อน
Phase 1 — หยุดเลือด (stop the bleeding)
- แก้ transaction bug — ทุก controller ที่ใช้
transaction: tเป็น arg ที่ 3 - เพิ่ม migration tool — แม้จะเริ่มจาก manual migration script ก็ย่าง ขอให้มี audit trail
- ลด pool size — จาก 100 เหลือ 30 + เพิ่ม monitoring
Phase 2 — ป้องกัน regression
- เพิ่ม linter ที่ตรวจ pattern
\.update\([^,]+,\s*\{[^}]+\},\s*\{(3-arg update) - เพิ่ม linter ที่ตรวจ duplicated column declarations
- เพิ่ม DB schema drift detection (compare model def vs actual DB)
Phase 3 — ปรับ foundation
- canonical employee ID — introduce
employeeIdUUID stable - relational
user_rolesjoin table - column encryption สำหรับ PII
Phase 4 — modernization เชิงโครงสร้าง
- split schema per bounded context (identity / payroll / leave / welfare / fund / attendance)
- anti-corruption layer สำหรับ HRMI
- event sourcing / outbox pattern สำหรับ cross-domain write
รายละเอียดเชิงกลยุทธ์อยู่ใน Volume 15 (Modernization Roadmap)
Cross-reference
| เรื่อง | Volume ที่เกี่ยวข้อง |
|---|---|
| Business logic ของแต่ละ domain | Volume 12 |
| Security / PII / encryption | Volume 10 |
| API ที่ expose model | Volume 11 |
| Tech debt register เชิงลึก | Volume 15 |
| Modernization roadmap | Volume 16 |
สรุป Volume 9
Data layer ของ VTRC มี topology ซับซ้อน (4 logical DBs, 3 services, 2 engines) แต่การ implement มีหลายจุดที่ผิดธรรมดา:
- transaction bug ลามทั้งระบบ → data integrity ไม่ guaranteed
- schema management ไม่มี migration → drift อย่างเงียบ
- 3+ PK strategies → join ข้าม domain ยาก
- PII ไม่ encrypt → data leak risk สูง
สิ่งที่ทำได้ดี:
- PK strategy ใช้ UUID ทุกที่
- money field เป็น DECIMAL
- charset/collate มี (ถึงแม้จะ mixed)
- index declaration มีบาง model ที่ composite + unique
Volume 9 นี้เป็น foundation สำหรับ volume อื่นที่จะอ้างถึง data layer — โดยเฉพาะ Volume 9 (Security), Volume 11 (Business Logic), Volume 14 (Tech Debt)
← กลับไป 8.0 ภาพรวม Volume หรือ Volume 3 · vtrc-api Deep Dive