Skip to content

5.1 · Repository map + รวมรวม dead code

บทนี้วาดแผนผังของ centralize-api/ ทั้ง repository พร้อมระบุ dead code และไฟล์ที่ตกค้างจาก NestJS starter template เพื่อให้อ่านและค้นหาโค้ดได้ง่ายขึ้น และทราบว่าไฟล์ไหนไม่ควรแก้เพราะเป็นขยะ


ภาพรวมขนาด

หมวดจำนวนไฟล์รวม LOC
Controllers4 (app, employee, hrPaySlip, hrTimeTemp)218
Services71,019
Entities7 (+ 1 index)200
Modules699
DTOs5 (+ 1 index)276
Repositories380
Root + libs + config + docs12523
รวม TypeScript44~2,219

เปรียบเทียบขนาดกับ service อื่นในระบบ — vtrc-api ใหญ่ที่สุดที่ประมาณ 25,800 LOC, cu-central-api อยู่ในระดับกลาง, ส่วน centralize-api ตัวนี้เล็กที่สุด (ประมาณ 2,219 LOC)


แผนผังไดเรกทอรี (depth 3 ของ src/)

centralize-api/
├── .env                              ← committed มี prod creds (บท 5.2 + 5.8)
├── .eslintrc.js
├── .prettierrc
├── Dockerfile                        ← ใช้ yarn install ทั้งที่เป็น npm repo (bug)
├── docker-compose.yml
├── nest-cli.json                     ← Swagger plugin config
├── package.json                      ← NestJS 9, TypeORM 0.3, mssql 9
├── tsconfig.json                     ← strictNullChecks: false
├── tsconfig.build.json
├── README.md                         ← default NestJS starter (ไม่มีเนื้อหา-specific)
├── config/
│   └── configuration.ts              ← env var loader 13 LOC
├── docs/
│   └── document.config.ts            ← Swagger DocumentBuilder 10 LOC
├── libs/
│   ├── common/
│   │   └── validator-date.ts         ← IsOnlyDate ไม่ได้ใช้ (dead)
│   ├── exception/
│   │   └── http-exception.filter.ts  ← จับแค่ HttpException
│   ├── jwt/
│   │   ├── jwt-auth.guard.ts         ← global guard ที่ถูก bypass ทุก route (บท 5.5)
│   │   └── jwt.strategy.ts           ← secretOrKey ผิดประเภท (บท 5.5)
│   └── log/
│       └── logs.middleware.ts        ← custom logger I3GatewayLogger
├── src/
│   ├── main.ts                       ← bootstrap + globals (บท 5.2)
│   ├── app.module.ts                 ← root module + 2 TypeORM connections (บท 5.2)
│   ├── app.controller.ts             ← root controller 1 endpoint
│   ├── app.controller.spec.ts        ← empty test (dead)
│   ├── app.service.ts                ← org hierarchy CTE 130 LOC
│   ├── dto/
│   │   ├── employee.dto.ts           ← DTOs ครอบคลุม EmployeeController (14 GET)
│   │   ├── hrTimeTemp.dto.ts         ← time attendance DTOs
│   │   ├── hrpayslip.dto.ts          ← EmpCodeListDto 1 class
│   │   ├── test.dto.ts               ← CreateCatDto (dead — โค้ดค้างจาก NestJS starter)
│   │   └── index.ts
│   ├── entities/
│   │   ├── employeeGroup.entity.ts        ← center connection
│   │   ├── employeeGroup.nbc.entity.ts    ← NBC connection (inherit)
│   │   ├── employeeLevel.entity.ts        ← center
│   │   ├── employeeLevel.nbc.entity.ts    ← NBC
│   │   ├── technicalPosition.entity.ts    ← center
│   │   ├── technicalPosition.nbc.entity.ts← NBC
│   │   ├── user.entity.ts                 ← DEAD — not registered (บท 5.6)
│   │   └── index.ts
│   └── modules/
│       ├── employee/                 ← 1 controller 14 GET endpoints + 1 service 605 LOC
│       │   ├── employee.controller.ts
│       │   ├── employee.service.ts
│       │   └── employee.module.ts
│       ├── employeeGroup/            ← sub-module ไม่มี controller
│       ├── employeeLevel/            ← sub-module ไม่มี controller
│       ├── hrPaySlip/                ← 1 controller 1 POST endpoint
│       ├── hrTimeTemp/               ← 1 controller 1 POST endpoint
│       └── technicalPosition/        ← sub-module ไม่มี controller
└── test/
    ├── app.e2e-spec.ts               ← broken — expects 'Hello World!' แต่จริงคือ SQL rows
    └── jest-e2e.json

จุดสังเกต

  • src/modules/employeeGroup/, employeeLevel/, technicalPosition/ เป็น sub-module ที่มี service + module แต่ ไม่มี controller ของตัวเอง — service ของทั้งสามถูก inject เข้าไปใน EmployeeController แทน (บท 5.3)
  • libs/ ใช้สำหรับโค้ด cross-cutting (auth, logging, exception, validator) ไม่ใช่ module ของ domain
  • ไม่มี migrations/ directory — TypeORM ใช้แค่ connection ไม่ได้ทำ schema migration (บท 5.2)

package.json — version ที่ใช้

2:36:centralize-api/package.json
  "dependencies": {
    "@nestjs/common": "^9.0.0",
    "@nestjs/config": "^2.2.0",
    "@nestjs/core": "^9.0.0",
    "@nestjs/passport": "^9.0.0",
    "@nestjs/platform-express": "^9.0.0",
    "@nestjs/swagger": "^6.1.3",
    "@nestjs/typeorm": "^9.0.1",
    "class-transformer": "^0.5.1",
    "class-validator": "^0.13.2",
    "moment": "^2.29.4",
    "mssql": "^9.1.1",
    "passport": "^0.6.0",
    "passport-jwt": "^4.0.0",
    "passport-local": "^1.0.0",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^7.2.0",
    "typeorm": "^0.3.10"
  },

สิ่งที่ควรทราบ

  • @nestjs/* 9.x — NestJS 9 release เดือน พฤษภาคม 2022 (ปัจจุบันเวอร์ชั่น 11.x ออกแล้ว ตามหลัง 2 major)
  • typeorm 0.3.10 — เวอร์ชั่น 0.3 ล่าสุดปลายรองรับทะเบียนวันที่ 2026 อยู่ที่ 0.3.20+
  • mssql 9.1.1 — Microsoft driver ตามหลัง 2 major (ปัจจุบันเวอร์ชั่น 11.x) มี known issue เรื่อง tedious driver
  • moment 2.29.4 — deprecated อย่างเป็นทางการ เปลี่ยนเป็น date-fns หรือ dayjs
  • passport-local อยู่ใน deps แต่ไม่ถูก import ที่ไหน — dead dependency (บท 5.5)
  • ไม่มี axios, got, node-fetch → service ไม่ได้เรียก HTTP external ใด ๆ
  • ไม่มี @nestjs/schedule → ไม่มี cron
  • ไม่มี helmet, compression, cookie-parser → ขาดการเตรียมพร้อมสำหรับ production
8:25:centralize-api/package.json
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/src/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },

จุดสำคัญ

  • start:prod = node dist/src/main ไม่มี NODE_ENV injection ใน script → env var ต้องตั้งภายนอก (docker-compose หรือ systemd)
  • lint ใช้ --fix เป็น default → อันตรายถ้ารันใน CI เพราะจะ modify file แทนที่จะแค่ตรวจ
  • มี test, test:watch, test:cov, test:debug, test:e2e ครบ แต่ไฟล์ test ที่มีคือ empty spec + broken e2e (ดู §"Dead code")

tsconfig.json — type safety ปิดเกือบหมด

1:21:centralize-api/tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  },
}

การตั้งค่าที่ทำให้ type safety อ่อนลง

ตัวเลือกค่าผลกระทบ
strictNullChecksfalseTypeScript ไม่ตรวจ null/undefined safety — obj.field ที่อาจเป็น null จะ compile ผ่าน
noImplicitAnyfalseอนุญาต implicit any — function parameter ที่ไม่มี type จะเป็น any โดยอัตโนมัติ
strictBindCallApplyfalseไม่ตรวจ bind/call/apply argument type
forceConsistentCasingInFileNamesfalseimport path ตัวเล็ก-ใหญ่ผิดได้ (เสี่ยงกับ filesystem case-sensitive เช่น Linux)
ไม่มี strict: trueumbrella strict mode ไม่เปิด

experimentalDecorators + emitDecoratorMetadata จำเป็นสำหรับ NestJS เพราะ decorator ต้องการ metadata ในขณะรัน (runtime)

.eslintrc.js ก็ปิด @typescript-eslint/no-explicit-any: 'off' เสริม → any ใช้ได้อย่างเสรี พบจริงใน jwt.strategy.ts, repositories ทั้ง 3


nest-cli.json — Swagger plugin

1:18:centralize-api/nest-cli.json
{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/swagger",
        "options": {
          "dtoFileNameSuffix": [".dto.ts"],
          "classValidatorShim": true,
          "introspectComments": true
        }
      }
    ]
  }
}

plugin นี้อ่าน DTO class ที่ลงท้ายด้วย .dto.ts แล้วสร้าง @ApiProperty() ให้อัตโนมัติจาก TypeScript type — เป็นเหตุผลที่ DTOs ใน src/dto/ ไม่ต้องเขียน decorator เองทุกบรรทัด (บท 5.3)

introspectComments: true → JSDoc comment เหนือ class/method จะถูกดึงไปเป็น Swagger description ด้วย


Dockerfile bug — yarn ใน npm repo

1:20:centralize-api/Dockerfile
FROM node:16 AS development
WORKDIR /usr/src/app
COPY package*.json ./
RUN yarn install --ignore-engines
COPY . .
CMD ["npm", "run", "start:dev"]

FROM node:16 as build
WORKDIR /usr/src/app
COPY package*.json ./
RUN yarn install --ignore-engines
COPY . .
RUN npm run build

FROM node:16 as production
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . .
COPY --from=build /usr/src/app/dist ./dist
CMD ["npm", "run", "start:prod"]

ปัญหา

  1. stage development + build ใช้ yarn install ทั้งที่ repo เป็น npm (มี package-lock.json pattern) → lockfile ที่ได้จาก yarn install จะไม่ตรงกับที่ dev ใช้ local ส่งผลให้ dependency tree ใน container อาจต่างจาก local อย่างเงียบ
  2. stage production ใช้ npm install --only=production สลับกลับมาเป็น npm — คำสั่ง --only=production ถูก deprecate ตั้งแต่ npm 7 แล้ว คำสั่งที่ถูกต้องคือ npm ci --omit=dev
  3. --ignore-engines ใน yarn stage ปิดเสียงเตือน engine check — อาจซ่อนปัญหาความเข้ากันได้ของ dependency กับ Node เวอร์ชั่นปัจจุบัน

ผลกระทบ — build ระหว่าง stage ไม่สามารถทำซ้ำได้ผลลัพธ์เดิม (reproducible) อย่างสมบูรณ์ และถ้ามี dependency ที่ติดตั้งไม่เหมือนกันระหว่าง dev กับ production จะเจอ bug ที่หายาก

การแก้ — เปลี่ยนทุก stage เป็น npm ci (clean install จาก package-lock.json) ลบ --ignore-engines และใช้ --omit=dev แทน --only=production


รวบรวม dead code

ไฟล์และโค้ดต่อไปนี้อยู่ใน repo แต่ ไม่ได้ใช้งานจริง — ลบได้โดยไม่กระทบ runtime แต่ทีมยังไม่ได้ลบเพราะอาจมีการคืนมาใช้ในอนาคต

รายการไฟล์หลักฐาน
CreateCatDto (class cat/breed/age)src/dto/test.dto.ts:1-17ค้างจาก NestJS starter template — ไม่ถูก import
IsOnlyDate validatorlibs/common/validator-date.ts:8-22grep ทั้ง repo ไม่พบการใช้งาน
UserEntity (table auth_user)src/entities/user.entity.ts:1-47ไม่ได้ export จาก entities/index.ts, ไม่ได้ register ใน module, table ไม่มีใน HRMI
getEmployeesTempPOakByEmployeeCode (commented)employee.service.ts:588-604โค้ดทั้ง method อยู่ใน /* */
EmployeeResponseDtodto/employee.dto.ts:72-102define แต่ไม่ได้เป็น return type ของ controller
passport-local dependencypackage.json:40import ไม่ได้เกิดขึ้นที่ไหน
app.controller.spec.ts empty testsrc/app.controller.spec.ts:18-20expect() ทั้งหมด comment ไว้
test/app.e2e-spec.ts broken e2etest/app.e2e-spec.tsทดสอบ return string 'Hello World!' แต่ service จริงคืน SQL rows → fail ทุกครั้ง

UserEntity — ตัวอย่าง dead code ที่น่าสนใจ

1:48:centralize-api/src/entities/user.entity.ts
import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm';

@Entity('auth_user')
@Unique('my_unique_auth_user', ['userName', 'email'])
export class UserEntity {
  @PrimaryGeneratedColumn('uuid')
  userId: string;
  // ... userName, password, firstName, lastName, email, userLevel, ...
}

จุดสังเกตที่บ่งบอกว่าเป็นโค้ดที่ค้างจาก starter template

  • ใช้ PrimaryGeneratedColumn('uuid') แบบ auto-generate — HRMI tables ใช้ string PK (เช่น EmplGrupID length 36) ไม่ใช่ auto-gen UUID
  • table name auth_user ไม่มีใน HRMI MSSQL เลย
  • decorator @Unique('my_unique_auth_user', ['userName', 'email']) มี prefix my_ บ่งบอก tutorial code
  • ไม่มี @ManyToOne/@OneToMany ใด ๆ ขาด FK ทั้งหมด
  • ไม่ได้ export จาก entities/index.ts → ไม่ถูก TypeORM load เข้า connection

เดาได้ว่าคนเขียน copy มาจาก Postgres starter เพราะ type hint บาง column comment ไว้ว่า 'timestamp with time zone' (Postgres type) แต่ MSSQL ไม่มี type แบบนั้น


สิ่งที่ขาด — ไฟล์ที่คาดว่าจะมีแต่ไม่มี

เมื่อเทียบกับ production NestJS service มาตรฐาน ไฟล์ต่อไปนี้ขาดหายไป

ไฟล์ / directoryปกติมีcentralize-api
src/migrations/TypeORM migration filesไม่มี — schema sync อาศัย HRMI เป็น source of truth
src/health/ หรือ HealthController/health endpointไม่มี — root /api รัน SQL หนักไม่เหมาะเป็น probe
.env.exampletemplate สำหรับ devไม่มี — มีแค่ .env จริงที่ committed (บท 5.8)
helm/ หรือ k8s/Kubernetes manifestไม่มี — deploy ผ่าน docker-compose
package-lock.json (committed)การติดตั้งที่ทำซ้ำได้ผลลัพธ์เดิม (reproducible)ไม่มีใน repo — Dockerfile ใช้ yarn install แทน
tests/ ที่มี unit จริงcoverageมีแค่ empty spec + broken e2e
LICENSEopen source licenseไม่มี

สรุปประเด็นสำคัญ

  • centralize-api เป็น TypeScript service ตัวเดียวในระบบ แต่ type safety ปิดเกือบหมด (strictNullChecks: false, noImplicitAny: false)
  • Dockerfile มี bug ร้าย — yarn install ใน npm repo ทำให้ build ไม่สามารถทำซ้ำได้ผลลัพธ์เดิม (reproducible) ระหว่าง stage
  • Dead code 6 รายการค้างอยู่ รวมถึง UserEntity ทั้งไฟล์และ CreateCatDto ที่เป็นโค้ดค้างจาก NestJS starter template
  • README.md เป็น default NestJS starter ไม่มีเนื้อหาเกี่ยวกับ project จริง — ถ้าเจอช่องว่างเอกสาร ให้เริ่มเขียนใหม่ทั้งหมด อย่าแก้ไขจากของเดิม