Skip to content

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
  • .env committed มี plaintext DB password และ SECRET_KEY ที่เป็น SSH pubkey fragment ไม่ใช่ JWT secret — เป็น broken crypto design
  • ทุก HRMI entity มี SourceDB column เช่นเดียวกับ 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 ที่ตรวจสอบ อาจมีสาเหตุได้หลายแบบ

  1. NestJS centralize-api ตัวนี้เป็น service ที่ยังไม่เสียบใช้งานจริงใน flow ที่ตรวจสอบ
  2. มี caller อื่นที่ไม่ใช่ vtrc-api (อาจเป็น direct admin tool ที่ไม่ได้เก็บใน repo ที่ตรวจสอบ)
  3. 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.1Repository map + สิ่งที่ตายแล้วทำความรู้จักโครงสร้างไฟล์ พร้อมระบุ dead code
5.2Bootstrap + TypeORM 2 connectionsเข้าใจ boot sequence + การเชื่อม MSSQL 2 databases
5.3REST API surface + DTOเห็นภาพทั้ง 17 endpoints ในตารางเดียว
5.4Service layer + SQL patternsเข้าใจ raw SQL pattern + copy-paste CTE + NOLOCK
5.5Authentication ที่ไม่ทำงานเห็นว่า guard ลงทะเบียนแล้วแต่ทุก route @Public()
5.6Entities + SourceDB tenancyรวบรวม 7 entities พร้อม SourceDB multi-tenant column
5.7Cross-cutting + Swaggerlogger, validation, filter, CORS, docs
5.8บัญชีหนี้ทางเทคนิคด้านความปลอดภัย4 Critical + 7 High severity พร้อม remediation
5.9Scorecard + 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, @1 positional 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 นี้ทั้งระบบ