2.1 · ภาพรวมสถาปัตยกรรม
บทนี้เล่าว่า VTRC มีกี่ส่วน แต่ละส่วนทำหน้าที่อะไร และทำไมถึงออกแบบเป็นแบบนี้
ถ้าอ่านบทเดียวในเล่ม 2 — อ่านบทนี้
กฎเหล็กข้อที่ 1 — ทุก request ผ่าน vtrc-api
vtrc-web ─┐
│ GraphQL over HTTPS
▼
vtrc-api ──► MariaDB (vtrc)
│ ──► MariaDB (vtrc-centralize)
│
│ GraphQL (internal) — END_POINT_CENTRALIZE
├──► cu-central-api ──► MSSQL (HRMI Center + NBC)
│ ──► LDAP/AD
│ ──► SMTP / SFTP
│
│ (caller ของ NestJS centralize-api ยังไม่ยืนยัน — ดูบท 5.9)
│ NestJS centralize-api ──► MSSQL (HRMI Center + NBC)
vtrc-rc-backoffice ─┘ (เหมือน vtrc-web)สำคัญ —
END_POINT_CENTRALIZEของ vtrc-api ชี้ไป cu-central-api GraphQL ไม่ใช่ NestJScentralize-api(port 3000 REST) — พอร์ตต้องแยกชั้น: CODE_DEFAULT ของ cu-central คือAPP_PORT=8080; CLIENT_TARGET ในvtrc-api/api/.env.exampleอาจเป็น8082; compose มัก map8000; DockerfileEXPOSE 9000≠ listen ดู บท 3.4 และ caller ใน บท 5.9
frontends (vtrc-web, vtrc-rc-backoffice) ไม่เคย เรียก centralize-api หรือ cu-central-api โดยตรง — เป็นกฎเหล็กที่บังคับใช้ด้วย network topology ไม่ใช่แค่ code convention
ดังนั้น vtrc-api จึงเป็นทั้ง API gateway + business logic + aggregation layer ไปพร้อมกัน ไม่มี API gateway แยกต่างหาก ไม่มี service mesh — Nginx ทำแค่ TLS termination กับ static hosting (vtrc-api/docker-compose.yml:14-34)
5 บริการหลัก
| บริการ | Stack | หน้าที่ | อายุ |
|---|---|---|---|
vtrc-web | React 16 + Webpack 4 + AntD v3 + Apollo v2 | frontend ฝั่งพนักงาน | เก่า (Critical-EOL) |
vtrc-rc-backoffice | React 16 + CRA + AntD v4 + MUI v5 + Recoil + Apollo v2 | frontend ฝั่ง HR | เก่า (Critical-EOL) |
vtrc-api | Node.js + Apollo GraphQL v2 + Sequelize 6 + Express | API แกนกลาง ทุกอย่างผ่านที่นี่ | เก่า (load-bearing) |
centralize-api | NestJS 9 + TypeORM + MSSQL | facade สำหรับ HR data สมัยใหม่ | ใหม่ (modernization reference) |
cu-central-api | Node 12 + Apollo GraphQL v2 + Sequelize + LDAP | identity service + raw HR data (เดิม) | เก่าที่สุด (Critical-EOL) |
ภาพใหญ่ — แบ่งตามชั้นความรับผิดชอบ
┌──────────────────────────────────────────────────────────────┐
│ Tier 1 — Edge (Nginx) │
│ • TLS termination │
│ • static hosting (frontend build) │
│ • reverse proxy → vtrc-api │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Tier 2 — Presentation (frontends) │
│ • vtrc-web (end-user) │
│ • vtrc-rc-backoffice (HR) │
│ • เลือก GraphQL endpoint ตอน runtime ด้วย window.location │
└──────────────────────────────────────────────────────────────┘
│
│ HTTPS GraphQL/REST + JWT + apiKey
▼
┌──────────────────────────────────────────────────────────────┐
│ Tier 3 — API Gateway + Business Logic (vtrc-api) │
│ • 4 replicas (APP_NO = 1, 2, 3, 5) │
│ • Apollo GraphQL + Express REST (สอง surface คู่กัน) │
│ • 3-layer internal: typeDefs → resolvers → controllers │
│ • @auth directive บังคับ RBAC + ABAC │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Tier 4 — Domain / Bounded Services │
│ • centralize-api (NestJS, modern) │
│ • cu-central-api (Node 12, legacy) │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Tier 5 — Persistence + Infra │
│ • MariaDB (2 schemas: vtrc + vtrc-centralize) │
│ • MSSQL (HRMI: dbHRMI_Center + dbHRMI_Center_NBC) │
│ • Redis (cache/session/OTP — แต่ใช้น้อย) │
│ • LDAP/AD (identity) │
│ • GCP (Drive + FCM) │
│ • SMTP, SFTP │
└──────────────────────────────────────────────────────────────┘ทำไมถึงเป็นแบบนี้ — บริบททางประวัติศาสตร์
VTRC ไม่ได้ออกแบบจากกระดานชอล์ก — มันโตมาจากการเติมทีละชิ้นตามความต้องการจริง
- ยุคที่ 1 (cu-central-api) — เริ่มจากระบบ HR ของจุฬาลงกรณ์มหาวิทยาลัย ที่สภากาชาดใช้ร่วมด้วย ตอนนั้นมีแค่ identity + LDAP + HR raw data จึงเป็น Node 12 + Sequelize + Apollo เก่า
- ยุคที่ 2 (vtrc-api + frontends) — สภากาชาดต้องการระบบ HR/welfare ของตัวเอง จึงสร้าง
vtrc-apiเป็น API ใหม่ที่เชื่อมไปยังcu-central-apiผ่านEND_POINT_CENTRALIZE(GraphQL; CLIENT_TARGET ตัวอย่างอาจใช้พอร์ต 8082 ในขณะที่ CODE_DEFAULT ของ cu-central เป็น 8080) แล้วเพิ่ม business logic ฝั่งตัวเอง - ยุคที่ 3 (NestJS centralize-api) — ทีมเริ่มปรับปรุงระบบสมัยใหม่ จึงตัดสินใจยกตัวอย่างใหม่ให้เป็น NestJS + TypeScript + Swagger ตั้งใจให้เป็น template สำหรับ service ใหม่ในอนาคต —
I3GatewayLogger(centralize-api/src/libs/log/logs.middleware.ts:8) เป็นลายเซ็นของทีม modernization
ข้อควรระวัง — caller ที่แท้จริงของ NestJS centralize-api (port 3000) ยังไม่ยืนยันจาก code ใน workspace นี้
END_POINT_CENTRALIZEของvtrc-apiชี้ไป cu-central-api ไม่ใช่ NestJS centralize-api ดูรายละเอียดใน บท 5.9
ผลคือระบบมี 2 generation อยู่ข้างกัน — legacy ที่ใช้งานจริง และ modernization island ที่ยังไม่ได้แทนที่อะไรเดิม
"Distributed monolith" — ลักษณะเด่นที่ต้องเข้าใจ
VTRC มีหลายบริการ แต่ ไม่ใช่ microservices ในความหมายปกติ
- ทุกบริการต้อง deploy พร้อมกัน — ไม่มี versioning แยก
- ไม่มี service discovery — ใช้ hardcoded env var (
END_POINT_CENTRALIZEชี้ไป cu-central-api) - ถ้า
cu-central-apiล่ม →vtrc-apiไม่สามารถให้บริการฟีเจอร์ HR ได้ — ไม่มี fallback ไม่มี circuit breaker - ทุก schema อยู่ใน database เดียวกัน (MariaDB host เดียว 2 schema)
ตอนอ่านโค้ด ให้คิดว่ามันเป็น "monolith ตัวเดียวที่แบ่ง process" จะเข้าใจง่ายกว่าคิดแบบ microservices
contract ระหว่างบริการ
| Edge | Protocol | Auth | ตัวอย่าง |
|---|---|---|---|
Browser → vtrc-api GraphQL | HTTPS POST | apiKey: <device> + Authorization: Bearer <jwt> | vtrc-api/api/src/index.js:33-44 |
Browser → vtrc-api REST | HTTPS GET | URL signed (1 ชม.) — :device/:timestamp/:hash | vtrc-api/api/src/routes.js:51-99 |
vtrc-api → cu-central-api | GraphQL (END_POINT_CENTRALIZE; CLIENT_TARGET ตัวอย่าง :8082, CODE_DEFAULT cu-central APP_PORT=8080) | header apiKey เมื่อส่งค่ามา (ไม่ default ใน client) | graphql.js:17-23 (GraphQLClient + optional apiKey) |
vtrc-api → cu-central-api (REST auth) | REST POST (END_POINT_CENTRALIZE_AUTH; ตัวอย่าง .env ใช้ :8082/auth) | form / header ตาม auth.js | auth.js:28 (/signin) |
NestJS centralize-api → MSSQL | TypeORM mssql | env DB creds | centralize-api/src/app.module.ts:36-38 |
vtrc-api → MariaDB | Sequelize | env DB creds (pool max 100) | vtrc-api/api/src/connector.js:8-27 |
cu-central-api → LDAP/AD | ldapjs bind | service account | cu-central-api/main-api/src/lib/auth/activeDirectory.connector.js:26-36 |
รายละเอียดเชิงลึกของแต่ละ edge — ดู บท 2.3 request lifecycle และ บท 2.4 external integrations
เคล็ดลับตอนทำงานจริง
อย่าตกใจที่เห็น "หลายบริการ"
เวลา debug ให้คิดว่าทั้งหมดคือ monolith ตัวเดียวที่แบ่ง process — vtrc-api → cu-central-api → MSSQL มองเป็น function call หลายชั้นก็ได้ ถ้าทำใจแบบนั้นจะ debug ง่ายขึ้นมาก
ระวัง "ดูเหมือน API gateway แต่ไม่ใช่"
Nginx ทำแค่ TLS + static — ไม่มี rate limit, ไม่มี auth ไม่มี routing logic ใดๆ ทุกอย่างผ่าน vtrc-api ที่เป็นแอป Node.js ตัวเดียว
"Modernization reference" ≠ production-ready
centralize-api เป็น NestJS + TypeScript สวยงาม แต่อย่าลืมว่า
- JWT guard ลงทะเบียนไว้ แต่ทุก route ปัจจุบันใช้
@Public()(centralize-api/src/modules/hrPaySlip/hrpayslip.controller.ts:17) - ไม่มี health check ไม่มี migration runner
- ลำพัง
centralize-apiจึงไม่พร้อมเป็น production service แบบ standalone
Volume 1 อธิบาย "ทำไม" Volume 2 อธิบาย "อย่างไร"
ถ้าเจอ policy ที่ดูผิดปกติ ในโค้ด — กลับไปอ่าน Volume 1 ก่อน เพราะส่วนใหญ่ไม่ใช่ technical decision แต่เป็น business rule ของ HR