การสร้างเครื่องมือจัดการโปรเจกต์ด้วย Prisma
ผมกำลังสร้างเครื่องมือจัดการโปรเจกต์แบบทำงานร่วมกันเหมือนกับ Trello โดยใช้ React, Express.js, PostgreSQL และ Socket.io
ขั้นตอนแรกคือการออกแบบ database schema ผมได้ออกแบบโมเดลไว้ 6 โมเดลเพื่อจัดการกับผู้ใช้งาน (users), โปรเจกต์ (projects), บอร์ด (boards), งาน (tasks), คอมเมนต์ (comments) และการแจ้งเตือน (notifications)
นี่คือตัวเลือกทางเทคนิคที่สำคัญและบทเรียนที่ได้รับจากการออกแบบ Prisma schema ของผม:
• ใช้ CUID สำหรับ ID
ผมใช้ @default(cuid()) สำหรับ ID ทั้งหมด ซึ่งดีกว่าการใช้ตัวเลขธรรมดาสำหรับ public URL เพราะช่วยป้องกันไม่ให้คนอื่นเดาได้ว่าคุณมีข้อมูลอยู่จำนวนเท่าใด
• การตั้งชื่อ Relation แบบชัดเจน
เมื่อมีสองฟิลด์ที่ชี้ไปยังโมเดลเดียวกัน Prisma อาจเกิดความสับสนได้ ตัวอย่างเช่น งาน (task) หนึ่งงานจะมีทั้งผู้สร้าง (creator) และผู้รับผิดชอบ (assignee) ซึ่งทั้งคู่เป็น Users ผมจึงต้องตั้งชื่อ relation เหล่านี้ให้ชัดเจนโดยใช้ชื่อใน @relation เพื่อป้องกันข้อผิดพลาดในการทำ migration
• Junction Tables สำหรับความสัมพันธ์แบบ Many-to-Many
ผู้ใช้งานหนึ่งคนสามารถอยู่ในหลายโปรเจกต์ได้ และหนึ่งโปรเจกต์ก็มีผู้ใช้งานได้หลายคน ผมจึงสร้างโมเดล ProjectMember ขึ้นมาเพื่อเชื่อมโยงทั้งสองเข้าด้วยกัน และได้เพิ่ม unique constraint ให้กับ userId และ projectId เพื่อป้องกันไม่ให้ผู้ใช้งานคนเดิมเข้าร่วมโปรเจกต์ซ้ำสองครั้ง
• ลำดับชั้นและตรรกะ (Hierarchy and Logic)
งาน (tasks) ไม่ได้อยู่ในโปรเจกต์โดยตรง แต่อยู่ในบอร์ด (boards) และบอร์ดก็อยู่ในโปรเจกต์อีกที วิธีนี้ช่วยให้การทำ drag-and-drop ทำได้ง่ายขึ้น เพราะหากต้องการย้ายงาน คุณเพียงแค่ต้องอัปเดต boardId เท่านั้น
• ความสัมพันธ์แบบ Nullable
ความสัมพันธ์บางอย่างเป็นแบบบังคับ (required) และบางอย่างเป็นแบบไม่บังคับ (optional) เช่น งานต้องมีผู้สร้างเสมอ ผมจึงไม่ใช้เครื่องหมายคำถาม แต่สำหรับผู้รับผิดชอบ (assignee) นั้นเป็นแบบไม่บังคับ ผมจึงใช้ User? และ String? ซึ่งคุณต้องระบุทั้งสองอย่างเพื่อให้ schema ทำงานได้
ข้อผิดพลาดที่พบบ่อย:
- การใช้ single quotes สำหรับค่า default ซึ่ง Prisma กำหนดให้ใช้ double quotes
- การลืมใส่เครื่องหมาย colon ใน syntax ของ references
- การเผลอใช้
@default(now())กับฟิลด์ ID โดยไม่ตั้งใจ
ขั้นตอนต่อไป ผมจะสร้าง backend ด้วย Express และตั้งค่า Socket.io สำหรับการอัปเดตข้อมูลแบบ real-time