เครื่องมือ Migration / Seed / Dump
ภาพรวม
Repo database-2026 ไม่ได้ทำหน้าที่เพียงเก็บ schema แต่เป็น ชุดเครื่องมือจัดการวงจรชีวิตของฐานข้อมูล
ทั้งการสร้าง migration, การ seed ข้อมูลตั้งต้น, การสร้างคอมเมนต์ประกอบคอลัมน์
และการเก็บ snapshot โครงสร้างจริงจากเซิร์ฟเวอร์
สิ่งที่ต้องเข้าใจก่อนแก้ไขอะไรก็ตามคือ schema ถูกจัดการผ่านสามช่องทางที่ปะปนกันอยู่
- Prisma (
prisma/schema.prismaและprisma/migrations/) ครอบคลุมเฉพาะ schemapublicและเฉพาะบางตารางเท่านั้น - Manual SQL (
manual-sql/) สำหรับตาราง คอลัมน์ และ trigger ที่ Prisma ไม่รู้จัก ต้อง apply ด้วย psql เอง - App migrations (
apps/<ชื่อแอป>/migrations/) สำหรับ schema แยกอย่างappointment,bulletinและloyaltyซึ่ง Prisma ไม่แตะเลย
โครงสร้างข้อมูลหลัก
คำสั่งใน package.json
| Script | คำสั่งจริง | ใช้ทำอะไร |
|---|---|---|
npm run init | prisma migrate reset แล้วตามด้วย up --file=init | สร้าง schema ครั้งแรก |
npm run status | prisma migrate status | ตรวจว่ามี migration ใดยังไม่ถูก apply |
npm run apply | prisma migrate deploy | apply migration ขึ้นฐานข้อมูล |
npm run up --file=<ชื่อไฟล์> | migrate diff เขียนไฟล์ down แล้วตามด้วย migrate dev --create-only | สร้างคู่ไฟล์ up และ down |
npm run down --file=<ชื่อไฟล์> | prisma db execute กับไฟล์ใน prisma/down/ | ย้อน migration |
npx ts-node seed.ts | อ่านไฟล์ตามตัวแปร $FILE_SEED ใน seed-data/ | seed ทีละไฟล์ |
npx ts-node seed-all.ts | ไล่ทุกไฟล์ใน seed-data/ | seed ทั้งชุด |
กลไกของ seed.ts และ seed-all.ts เรียบง่ายมาก คืออ่านไฟล์ SQL แล้วแยกคำสั่งด้วย split(';')
ก่อนส่งเข้า $executeRawUnsafe ทีละคำสั่ง ข้อจำกัดที่ตามมาคือไฟล์ seed จะมีเครื่องหมาย ;
อยู่ภายในสตริงไม่ได้
ลำดับการ seed
ลำดับมีความสำคัญเพราะมี FK ระหว่างตาราง ไฟล์ seed-data/01 ถึง 16 เรียงตามลำดับดังนี้
system_module → system_role → system_role_module → organization → user → line_oa →
line_user → audience → rich_menu → password_history → rich_message → campaign →
api_client → api_key → tracking_line_users → form_builder_rule
src/generate-comment.ts
เป็น custom Prisma generator ที่แปลงคอมเมนต์แบบ /// ใน schema ให้กลายเป็นคำสั่ง COMMENT
แล้วสร้างออกมาเป็น migration ให้อัตโนมัติ ใช้ lock file ร่วมกับ sha256 เพื่อตรวจว่าคอมเมนต์
เปลี่ยนแปลงหรือไม่ เครื่องมือนี้เป็น workaround ของ Prisma issue #8703 โดยต้อง apply เองเสมอ
และมีข้อจำกัดกับชื่อฟิลด์ที่ไม่ได้อยู่ในรูปแบบ snake case
schema-dumps/2026-07-24/
Snapshot โครงสร้างของ preprod ที่ได้จาก pg_dump --schema-only บน PostgreSQL 17.9
schema.sqlเก็บโครงสร้างเต็มtriggers-summary.txtสรุปหนึ่งบรรทัดต่อหนึ่ง triggernotify-functions.txtรวมทุกฟังก์ชันที่เรียกpg_notify
Dump ชุดนี้ครอบคลุมเฉพาะ schema public และ appointment ณ วันที่ทำ ยังไม่รวม bulletin
และ loyalty
เครื่องมือตรวจ drift
prisma-example-script/check-diff-schema-db.txt เก็บตัวอย่างคำสั่ง prisma migrate diff
สำหรับเทียบ schema.prisma กับฐานข้อมูลจริงในแต่ละสภาพแวดล้อม (local, UAT, production)
นี่คือเครื่องมือหลักในการหา drift ที่เกิดจากการ apply manual SQL
ส่วน prisma/ERD.md เป็น ERD แบบ mermaid ที่ ล้าสมัยแล้ว ยังใช้ชื่ออย่าง Organize
และมี Pdpa กับ AudienceMember ที่ไม่มีอยู่ในสคีมาปัจจุบัน จึงควรยึด schema.prisma เป็นหลัก
ไฟล์ที่เกี่ยวข้อง
package.json,seed.ts,seed-all.ts,README.md(คู่มือฉบับภาษาไทย)src/generate-comment.ts,src/dist/generate-comment.jsprisma/schema.prisma,prisma/migrations/,prisma/ERD.mdmanual-sql/0.select.sqlถึงmanual-sql/9.import_mapping.sqlapps/{appointment,bulletin,loyalty}/migrations/schema-dumps/2026-07-24/,prisma-example-script/check-diff-schema-db.txt.env.example— ตัวแปรDATABASE_URLและDATABASE_NAMEโดยตัวอย่างในไฟล์ยังเขียนเป็น mysql แต่ระบบจริงใช้ postgresql.gitignore— ระบุให้ ignoreprisma/migrationsและprisma/downดังนั้น migration ที่ถูก commit ไว้จึงมาจากการ force add เฉพาะบางไฟล์
จุดเชื่อมต่อกับ Service อื่น
- ทุก service ใช้ schema ที่ repo นี้กำหนด แต่ ไม่มี service ใดใช้ Prisma ตอน runtime โดย cms-api-go และ client-api-go ใช้ raw SQL ร่วมกับ sqlx หรือ GORM ส่วน worker-go และ webhook-go ก็ใช้แนวทางเดียวกัน ผลที่ตามมาคือการเปลี่ยนชื่อคอลัมน์จะไม่ถูกจับได้ตอน compile ต้องไล่แก้ query ในแต่ละ service เอง
- ข้อควรระวัง:
prisma migrateไม่รู้จัก manual SQL และ app schema การรันprisma migrate resetจะล้างเฉพาะpublicและทำให้ trigger ทั้งหมดหายไป จึงต้อง apply ไฟล์ในmanual-sql/ใหม่ทุกครั้ง (README มีหัวข้อ "วิธี Reinit" อธิบายไว้)