Volume 5 · centralize-api Deep Dive
Volume นี้เจาะลึก centralize-api — service ตัวเดียวในระบบที่ใช้ TypeScript + NestJS + TypeORM + MSSQL หน้าที่หลักคือเป็น ชั้นห่อหุ้มสำหรับอ่าน HR master data (HR data facade) จาก HRMI MSSQL โดยตรง และเปิด REST API พร้อม Swagger documentation ที่บริการอื่นไม่มี
ทำไมต้องมี Volume นี้
ตาม audit/02-architecture-map.md §2.3 centralize-api ถูก label เป็น "NestJS Central HR Data Service" แต่ label นั้นดูเหมือนไม่มีปัญหา จนกระทั่งลองอ่าน code จริงจะพบว่ารายละเอียดที่ซ่อนอยู่มีมาก
- เป็น service ตัวเดียวที่ read-only 100% — ไม่มี
INSERT/UPDATE/DELETEใน code เลย ไม่มี mutation endpoint แต่ใช้ raw SQL string ทั้งระบบ TypeORM ใช้แค่ connection management ไม่ได้ใช้ query builder - ระบบ authentication ลงทะเบียนเป็น global guard แต่ถูก bypass ทุก route เพราะทุก controller ใช้
@Public()— auth machinery ทั้งหมดจึงเป็น dead code ใน production .envcommitted มี plaintext DB password และ SECRET_KEY ที่เป็น SSH pubkey fragment ไม่ใช่ JWT secret — เป็น broken crypto design- ทุก HRMI entity มี
SourceDBcolumn เช่นเดียวกับ cu-central-api บ่งชี้ว่าเป็น multi-tenant pattern เดียวกัน - มี 17 endpoints ใน 4 controllers (Employee 14 GET + App 1 GET + PaySlip 1 POST + TimeTemp 1 POST) แต่ naming inconsistent (
posManage,posTechnical,getByEmpCode,getEmployeeDisaster,bankAccount) และมี dead code จาก NestJS starter template ค้างอยู่หลายจุด
ถ้า debug เรื่อง "ทำไม employee ของ org X ไม่ return" หรือ "ทำไม time attendance ของ NBC group หาย" ต้องเริ่มที่ Volume นี้ เพราะเป็นทางผ่านของ HR master data ทั้งหมด
ขอบเขตของ Volume
centralize-api/
├── .env committed มี prod creds (บท 5.2 + 5.8)
├── package.json NestJS 9, TypeORM 0.3, mssql 9 (บท 5.1)
├── tsconfig.json strictNullChecks: false (บท 5.1)
├── Dockerfile yarn install ทั้งที่เป็น npm repo (บท 5.1)
├── config/configuration.ts env var loader ไม่มี Joi (บท 5.2)
├── docs/document.config.ts Swagger config (บท 5.7)
├── libs/
│ ├── jwt/ guard + strategy ที่ไม่ทำงาน (บท 5.5)
│ ├── exception/ filter จับแค่ HttpException (บท 5.7)
│ ├── log/ custom logger ไม่มี rotation (บท 5.7)
│ └── common/ validator ที่ไม่ได้ใช้ (บท 5.1)
└── src/
├── main.ts bootstrap + globals (บท 5.2)
├── app.module.ts root module + 2 TypeORM connections (บท 5.2)
├── app.service.ts org hierarchy CTE 130 lines (บท 5.4)
├── dto/ 5 DTOs + 1 dead CreateCatDto (บท 5.3)
├── entities/ 7 entities (6 live + 1 dead UserEntity) (บท 5.6)
└── modules/
├── employee/ 14 GET endpoints — core (บท 5.3 + 5.4)
├── hrPaySlip/ 1 POST — payslip lookup (บท 5.3)
├── hrTimeTemp/ 1 POST — time attendance merge (บท 5.3 + 5.4)
├── employeeGroup/ sub-module ไม่มี controller (บท 5.6)
├── employeeLevel/ sub-module ไม่มี controller (บท 5.6)
└── technicalPosition/ sub-module ไม่มี controller (บท 5.6)ขนาดรวม ~2,219 LOC ใน 44 ไฟล์ TypeScript — เล็กมากเมื่อเทียบกับ vtrc-api (~25,800 LOC) แต่มีรายละเอียดที่ต้องเข้าใจก่อนแตะ code
ใครควรอ่าน
- Engineer ที่แก้ centralize-api — อ่านตามลำดับ 5.1 → 5.9 ทั้งหมด เพราะระบบเล็กและเชื่อมโยงกันหมด
- Engineer ที่ debug เรื่อง HR master data ผิด — ข้ามไป 5.4 (service layer + SQL) กับ 5.6 (entities + SourceDB)
- Engineer ที่จะปรับปรุงระบบ — อ่าน 5.8 (security debt) กับ 5.9 (scorecard + ownership) ก่อน ทั้งสองบทระบุจุดที่ต้อง refactor
- Security reviewer — อ่าน 5.5 (auth bypass) กับ 5.8 (security debt) ก่อน เพราะทั้งสองบทระบุช่องโหว่ที่ยังไม่ได้รับการแก้ไขในปัจจุบัน
ข้อควรระวังสำคัญ — ที่มาของ caller ใน production ยังไม่ยืนยัน
การสำรวจ code ใน vtrc-api/api/src/ พบว่า END_POINT_CENTRALIZE ชี้ไป GraphQL ของ cu-central-api (CLIENT_TARGET ใน .env.example เป็น http://localhost:8082/api/graphql) — ไม่ใช่ NestJS centralize-api ตัวนี้ที่รัน port 3000
vtrc-api/.env.example:36 END_POINT_CENTRALIZE="http://localhost:8082/api/graphql"
vtrc-api/api/src/centralize/controllers/graphql.js:21 new GraphQLClient(END_POINT_CENTRALIZE, ...)
vtrc-api/api/src/centralize/controllers/auth.js:28 ${END_POINT_CENTRALIZE_AUTH}/signinพอร์ต cu-central: CODE_DEFAULT APP_PORT=8080, CLIENT_TARGET อาจเป็น 8082, EXPOSE 9000 ≠ listen, compose มัก 8000
ผลคือ ไม่พบ code ใน vtrc-api ที่ยิงไป /api/employee, /api/hrPaySlip, /api/hrTimeTemp ของ NestJS centralize-api โดยตรง ในระดับ source code ที่ตรวจสอบ อาจมีสาเหตุได้หลายแบบ
- NestJS centralize-api ตัวนี้เป็น service ที่ยังไม่เสียบใช้งานจริงใน flow ที่ตรวจสอบ
- มี caller อื่นที่ไม่ใช่ vtrc-api (อาจเป็น direct admin tool ที่ไม่ได้เก็บใน repo ที่ตรวจสอบ)
- env ใน
.env.exampleเป็น CLIENT_TARGET ตัวอย่าง — ไม่ใช่ CODE_DEFAULT ของ cu-central (APP_PORT=8080) และไม่ใช่ NestJS port 3000
Volume นี้จึงเขียนตามที่ code evidence บอก และ flag จุดนี้ไว้ใน 5.9 (Scorecard + ownership) พร้อมคำแนะนำในการ verify เพิ่มเติมก่อนตัดสินใจ refactor
สิ่งที่ Volume นี้ไม่ cover
- HRMI MSSQL schema ทั้งหมด — cover ใน Volume 8 (Data Model & Persistence) โดยเฉพาะบท 8.1 (Persistence topology) และ 8.9 (Identity across databases) เพราะ schema ของ HRMI เป็นของระบบ CU/RC ไม่ใช่ของ VTRC
- vtrc-api → cu-central-api bridge mechanics — cover ใน Volume 3.4 (The centralize bridge) เพราะ bridge logic อยู่ฝั่ง vtrc-api (โฟลเดอร์ชื่อ
centralize/แต่ปลายทางคือ cu-central-api ไม่ใช่ NestJS service นี้) - cu-central-api เปรียบเทียบเชิงลึก — cover ใน Volume 4 ทั้ง volume
โครงสร้างบท
| บท | ชื่อ | จุดประสงค์ |
|---|---|---|
| 5.1 | Repository map + สิ่งที่ตายแล้ว | ทำความรู้จักโครงสร้างไฟล์ พร้อมระบุ dead code |
| 5.2 | Bootstrap + TypeORM 2 connections | เข้าใจ boot sequence + การเชื่อม MSSQL 2 databases |
| 5.3 | REST API surface + DTO | เห็นภาพทั้ง 17 endpoints ในตารางเดียว |
| 5.4 | Service layer + SQL patterns | เข้าใจ raw SQL pattern + copy-paste CTE + NOLOCK |
| 5.5 | Authentication ที่ไม่ทำงาน | เห็นว่า guard ลงทะเบียนแล้วแต่ทุก route @Public() |
| 5.6 | Entities + SourceDB tenancy | รวบรวม 7 entities พร้อม SourceDB multi-tenant column |
| 5.7 | Cross-cutting + Swagger | logger, validation, filter, CORS, docs |
| 5.8 | บัญชีหนี้ทางเทคนิคด้านความปลอดภัย | 4 Critical + 7 High severity พร้อม remediation |
| 5.9 | Scorecard + ownership + comparison | สรุป + เปรียบเทียบกับ vtrc-api/cu-central-api |
สิ่งที่ควรทราบก่อนอ่าน
- NestJS 9 — module/controller/service pattern, DI, decorator-based metadata (
@Module,@Injectable,@Get,@Public) - TypeORM 0.3 —
@Entity,@Column,EntityManager.query(),BaseEntityแบบ active record - MSSQL + tedious driver —
@0,@1positional parameter binding,WITH (NOLOCK)hint, cross-database query syntax (dbHRMI_Center.dbo.tableName) - Swagger / OpenAPI —
@ApiTags,@ApiBearerAuth,@ApiOperation, DocumentBuilder
ถ้าไม่คุ้นกับ NestJS แนะนำอ่าน NestJS overview ส่วน dependency injection ก่อน เพราะ centralize-api ใช้ pattern นี้ทั้งระบบ