แบบฝึกหัดต่อยอด
สิ่งที่จะสร้าง
หัวข้อที่มีชื่อว่า “สิ่งที่จะสร้าง”TaskFlow ใช้งานได้ แต่จงใจยังไม่เสร็จสมบูรณ์ — ยังมีส่วนของโปรดักต์ที่เห็นได้ชัดอีกมากที่ต้องเพิ่ม และทุกส่วนคือโอกาสในการนำ pattern ที่คุณสร้างไว้แล้วกลับมาใช้ซ้ำ ด้านล่างคือแบบฝึกหัดต่อยอดแปดข้อ เรียงคร่าว ๆ จากง่ายไปยาก แต่ละข้อเป็นฟีเจอร์จริงพร้อมคำแนะนำหนึ่งถึงสองประโยค ไม่ใช่การพาทำแบบละเอียด — จุดประสงค์คือให้ คุณ ตัดสินใจว่าโค้ดควรอยู่ตรงไหน โดยใช้โมดูลที่คุณเขียนไว้แล้วเป็นแผนที่
วิธีที่ดีที่สุดในการรู้ว่าคุณเข้าใจสถาปัตยกรรมจริง ๆ คือการต่อยอดโดยไม่มีทูทอเรียลคอยจับมือ แบบฝึกหัดแต่ละข้อด้านล่างเลือกมาเพราะบังคับให้คุณย้อนกลับไปผ่านรอยต่อเฉพาะจุดของระบบ — Redis pub/sub event ใหม่, cache key ใหม่ที่ต้อง invalidate, service-layer guard ใหม่, frontend reducer case ใหม่ — ดังนั้นคุณจึงไม่ได้เรียน pattern ใหม่ แต่กำลังพิสูจน์ว่าคุณเป็นเจ้าของ pattern จากคอร์สนี้จริง
ลงมือสร้าง
หัวข้อที่มีชื่อว่า “ลงมือสร้าง”1. Activity log ของ card (ง่าย)
หัวข้อที่มีชื่อว่า “1. Activity log ของ card (ง่าย)”เพิ่มตาราง activity (card id, actor, verb, timestamp) แล้วเขียนหนึ่งแถวทุกครั้งที่มีการสร้าง ย้าย หรือแก้ไข card ยิง event activity.created ผ่าน redis-backplane ควบคู่ไปกับการเปลี่ยนแปลงต้นเหตุ เพื่อให้ activity feed ในอนาคตแสดงผลแบบสดได้ เริ่มจากแบบอ่านอย่างเดียวก่อน: endpoint ที่คืน activity N รายการล่าสุดของบอร์ด
2. วันครบกำหนดและไฮไลต์การ์ดที่เลยกำหนด (ง่าย)
หัวข้อที่มีชื่อว่า “2. วันครบกำหนดและไฮไลต์การ์ดที่เลยกำหนด (ง่าย)”เพิ่มคอลัมน์ due_at ที่เป็น nullable ลงใน cards รับค่านี้ใน handler สร้าง/อัปเดตจาก cards แล้วใส่ลงใน payload ของ card ฝั่ง frontend ให้ component ของ card ใน drag-drop แสดงสไตล์ “overdue” เมื่อ due_at เป็นเวลาในอดีต ไม่มี infrastructure ใหม่ — นี่เป็นแบบฝึกหัด schema-บวก-render ล้วน ๆ ไว้อุ่นเครื่อง
3. คอมเมนต์บน card แบบเรียลไทม์ (ปานกลาง)
หัวข้อที่มีชื่อว่า “3. คอมเมนต์บน card แบบเรียลไทม์ (ปานกลาง)”เพิ่มตาราง comments ที่ key ด้วย card id, endpoint POST /cards/:id/comments และ event comment.created บน channel ของบอร์ด เพื่อให้หน้ารายละเอียด card ที่เปิดอยู่อัปเดตแบบสด — ก็คือเส้นทาง ws-endpoint → live-sync ที่คุณสร้างไว้สำหรับการย้าย card นั่นเอง เอามาใช้กับ entity ตัวที่สอง นำแนวคิด reconcile-by-id จาก live-sync กลับมาใช้ซ้ำ
4. Full-text search ทั่วทุก card (ปานกลาง)
หัวข้อที่มีชื่อว่า “4. Full-text search ทั่วทุก card (ปานกลาง)”เพิ่มคอลัมน์ tsvector ของ Postgres (หรือแบบ generated) ที่คร่อม title และ description ของ card, index แบบ GIN และ endpoint GET /boards/:id/search?q= ที่จัดอันดับผลลัพธ์ด้วย ts_rank นี่คือก้าวถัดไปตามธรรมชาติจากงาน indexing ใน indexes-ordering — วินัยเดิม index ชนิดใหม่ ตัดสินใจว่าผลลัพธ์ควร cache ผ่าน cache-reads ได้ หรือเฉพาะเจาะจงกับ query เกินกว่าจะคุ้ม
5. Role ของ board และ RBAC (ปานกลาง–ยาก)
หัวข้อที่มีชื่อว่า “5. Role ของ board และ RBAC (ปานกลาง–ยาก)”เพิ่มตาราง board_members ที่มี role ต่อผู้ใช้ (owner, editor, viewer) แล้วบังคับใช้ role นั้นที่ service layer — viewer GET ได้แต่ PATCH ไม่ได้ มีแต่ owner เท่านั้นที่ลบได้ วางการเช็คไว้ตรงที่ middleware วาง authentication ไว้แล้ว เพื่อให้ทุก handler ที่ scope กับบอร์ดผ่าน authorization gate เดียว แทนที่จะกระจายการเช็ค if role == ไปทั่ว boards, columns และ cards
6. UI สำหรับสร้าง column และ card (ปานกลาง–ยาก)
หัวข้อที่มีชื่อว่า “6. UI สำหรับสร้าง column และ card (ปานกลาง–ยาก)”ตอนนี้ island seed columns และ cards ผ่าน curl ซึ่ง compose-full ชี้ไว้ว่าเป็นขอบเขตจริงของคอร์ส ปิดช่องนี้: เพิ่มฟอร์ม “add column” และ “add card” ลงใน Kanban island จาก drag-drop โดย post ไปยัง endpoint จาก columns และ cards ด้วย pattern optimistic-แล้ว-reconcile แบบเดียวกับที่การลากใช้อยู่ เพื่อให้ item ใหม่ปรากฏทันทีแล้วค่อยซิงก์
7. Playwright e2e สำหรับ live sync สองแท็บ (ยาก)
หัวข้อที่มีชื่อว่า “7. Playwright e2e สำหรับ live sync สองแท็บ (ยาก)”เดโมชูโรงของคอร์ส — ลาก card ในแท็บหนึ่ง แล้วดูการ์ดย้ายในแท็บที่สอง — เราตรวจด้วยมือมาตลอด ถึงเวลาทำให้เป็นอัตโนมัติ: Playwright test ที่เปิด browser context สองตัวบนบอร์ดเดียวกัน ลาก card ในตัวแรก แล้ว assert ว่าการ์ดย้ายในตัวที่สองภายใน timeout นี่คือส่วนเติมเต็มระดับ end-to-end ให้กับ frontend-tests ระดับ unit และได้ใช้งานเส้นทาง WebSocket จริง ไม่ใช่ mock
8. แก้ conflict แบบ optimistic concurrency ตอนย้าย card (ยาก)
หัวข้อที่มีชื่อว่า “8. แก้ conflict แบบ optimistic concurrency ตอนย้าย card (ยาก)”ทุกวันนี้ ผู้ใช้สองคนที่ลาก card ใบเดียวกันพร้อมกันจะแข่งกัน — last write wins แบบเงียบ ๆ เพิ่มคอลัมน์ version (หรือ updated_at) ลงใน cards กำหนดให้ client ส่ง version ที่ตัวเองเห็นมาด้วย แล้วปฏิเสธการย้ายที่ version ล้าสมัยด้วย 409 Conflict จาก error-handling จากนั้นจัดการ 409 นั้นฝั่ง frontend: ย้อน optimistic move จาก drag-drop กลับ แล้ว re-fetch นี่ยากที่สุดเพราะต้องแตะทั้ง schema, service layer, error type และ optimistic-UI reducer พร้อมกัน — ครบทั้งความกว้างของสแตก
ตรวจสอบผล
หัวข้อที่มีชื่อว่า “ตรวจสอบผล”คุณไม่จำเป็นต้องทำครบทั้งแปดข้อ เลือกสองข้อ — ในอุดมคติคือข้อจากฝั่งง่ายไว้สร้างแรงส่ง และข้อจากฝั่งยากไว้ยืดตัว — แล้วทำแต่ละข้อด้วยวิธีเดียวกับที่คอร์สนี้ทำกับทุกโมดูล: เขียน migration ก่อน เพิ่ม #[sqlx::test] จาก backend-tests ที่พิสูจน์พฤติกรรมใหม่ แล้วค่อยต่อ frontend ถ้าฟีเจอร์ใหม่ของคุณนำ event, cache key หรือ service guard เดิมกลับมาใช้ซ้ำ แทนที่จะประดิษฐ์ตัวขนานขึ้นมาใหม่ แสดงว่าคุณต่อยอดสถาปัตยกรรมได้ถูกต้องแล้ว
แบบฝึกหัดต่อยอดแปดข้อ ง่ายไปยาก: card activity log พร้อม event activity.created, due dates พร้อม overdue highlight, card comments แบบเรียลไทม์, Postgres full-text search, board roles/RBAC ที่ service layer, UI สร้าง column/card, Playwright test สำหรับ live-sync สองแท็บ และ optimistic-concurrency conflict resolution บนการย้าย แต่ละข้อจงใจพาคุณย้อนกลับไปผ่านรอยต่อที่คุณสร้างไว้แล้ว ต่อไป: TaskFlow จะไปทางไหนต่อจากนี้ และ Real-World Projects อื่น ๆ