ออกแบบ Shared Inbox: เหตุใด Message จึงควรเป็น Primary Key
เจาะลึกการออกแบบสถาปัตยกรรมระบบ Shared Inbox จาก Onsites AI ที่ชี้ให้เห็นว่าการใช้ Message เป็น Primary Key แทน Conversation ช่วยลดปัญหาข้อมูลสูญหาย

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- การใช้ Conversation เป็นแหล่งข้อมูลหลักมักสร้างปัญหาเมื่อต้องเพิ่มช่องทางติดต่อใหม่
- Message คือความจริงที่เกิดขึ้นครั้งเดียวและไม่มีวันเปลี่ยนแปลง เหมาะสำหรับเป็น Primary Key ธรรมชาติ
- การจัดเก็บสถานะการส่งอีเมลแบบรายข้อความช่วยให้ตรวจสอบความผิดพลาดได้แม่นยำขึ้น
- Onsites AI แชร์โครงสร้างฐานข้อมูลที่ช่วยป้องกันข้อความซ้ำซ้อนจาก Webhook retries
การพัฒนาโปรแกรมประเภท Shared Inbox ส่วนใหญ่มักเริ่มต้นด้วยการสร้างตาราง conversations และมักจะต้องมานั่งเสียใจทีเดียวภายหลัง โดยบทความจาก Onsites AI บนแพลตฟอร์ม Dev.to ได้นำเสนอโครงสร้างฐานข้อมูลที่ผ่านการทดสอบจริงกับปริมาณงานซัพพอร์ตจริง ซึ่งช่วยแก้ปัญหาความยุ่งยากในการควบรวมข้อมูลเมื่อระบบเติบโตขึ้น
ในความเป็นจริงแล้ว Conversation ไม่ใช่สิ่งที่เป็นความจริงแท้ในตัวมันเอง แต่เป็นสิ่งที่ถูกประมวลผลขึ้นมาภายหลังจากการพิจารณาว่าข้อความใดควรถูกจัดกลุ่มให้อยู่ด้วยกัน หากนักพัฒนาเลือกเก็บ Conversation เป็นแหล่งข้อมูลหลัก (source of truth) ทุกครั้งที่มีช่องทางการสื่อสารใหม่เพิ่มเข้ามา จะต้องเขียนชุดคำสั่งสำหรับทำ Merge เพื่อปรับเปลี่ยนแถวข้อมูลในตาราง ซึ่งการควบรวมข้อมูลถือเป็นกระบวนการที่ห้ามทำข้อมูลสูญหายโดยเด็ดขาด
ในทางตรงกันข้าม ข้อความหรือ Message คือสิ่งที่เดินทางมาถึงระบบเพียงครั้งเดียวและไม่มีวันเปลี่ยนแปลง มันจึงเป็นธรรมชาติอย่างยิ่งที่จะทำหน้าที่เป็น Primary Key ของระบบ โดยโครงสร้างฐานข้อมูลที่แนะนำประกอบด้วยตารางหลักดังนี้
- messages: เก็บข้อมูลข้อความที่ไม่เปลี่ยนแปลง เช่น id, channel, external_id, direction, sent_at, body และ raw
- contacts: ข้อมูลผู้ส่งที่อาจมีได้หลายข้อความ
- threads: การจัดกลุ่มที่สามารถสร้างใหม่ได้ตลอดเวลา
- thread_messages: ตารางเชื่อมโยงแบบ append-only

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
นอกจากนี้ ระบบยังต้องจัดการกับความท้าทายในเรื่องตัวตนของผู้ใช้งาน เมื่อลูกค้าคนเดิมติดต่อเข้ามาจากหลากหลายอีเมล เช่น ana@company.com ตามด้วยอีเมลส่วนตัว และอีเมลใหม่หลังการรีแบรนด์ ระบบการจับคู่แบบตรงตัวจะทำให้เกิด Contact และ Thread แยกออกจากกันถึงสามชุด ทางออกที่ดีคือการมีคิวแนะนำ (suggestion queue) ที่ให้มนุษย์เข้ามาช่วยยืนยันกรณีที่กำกวม แทนที่จะปล่อยให้ระบบใช้ Regular Expression ตัดสินใจทั้งหมด
ในส่วนของการจัดส่งข้อความขาออก สถานะของอีเมลสามารถเป็นได้ทั้ง queued, sent, delivered, bounced หรือ complained ซึ่งวงจรชีวิตนี้เกิดขึ้นในระดับรายข้อความและรายผู้รับ การเก็บบันทึกสถานะว่าข้อความถูกส่งไปแล้วในระดับ Conversation จะทำให้เรามองไม่เห็นปัญหาสำคัญ เช่น กรณีที่ผู้รับสามคน แต่อีเมลตีกลับไปหนึ่งคน
ตาราง message_delivery ที่เก็บข้อมูล message_id, recipient, state, updated_at และ provider_id จะช่วยให้คำถามว่าลูกค้าได้รับข้อความหรือยัง กลายเป็นการquery ข้อมูลธรรมดาแทนที่จะเป็นปัญหา escalation ในทีมซัพพอร์ต
อีกหนึ่งจุดสำคัญคือการตั้งค่าให้ messages.external_id มีความเฉพาะตัว (unique) ในแต่ละช่องทางและทำการ dedupe ทุกครั้งที่มีการบันทึก เนื่องจากผู้ให้บริการภายนอกมักจะส่ง Webhook ซ้ำมาเสมอ หากไม่มีข้อจำกัดนี้ ระบบจะส่งข้อความตอบกลับซ้ำซ้อน ซึ่งเป็นข้อผิดพลาดที่ลูกค้าจะจดจำไปอีกนาน โครงสร้างเหล่านี้ถูกนำไปใช้จริงใน Onsites AI เครื่องมือ Shared Inbox, CRM และระบบออกใบแจ้งหนี้แบบเบาสำหรับทีมขนาด 3 ถึง 30 คน ที่สามารถเลือกใช้งานแบบ self-host หรือบนคลาวด์ก็ได้
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น