การออกแบบ Message
โมดูล foundations สอนคุณวิธีเปิด connection และส่ง bytes ไปมาทั้งสองทิศทาง แต่ connection ที่รับส่ง อะไรก็ได้ นั้นไม่ได้บอกคุณ เลย ว่า bytes เหล่านั้นมีความหมายอะไร โมดูลนี้ — การออกแบบ Message — คือจุดที่คุณเลิกส่ง text ดิบ ๆ แล้วเริ่มส่ง protocol
socket นั้นว่างเปล่าโดยตั้งใจ
หัวข้อที่มีชื่อว่า “socket นั้นว่างเปล่าโดยตั้งใจ”นี่คือสิ่งที่ไม่มีใครบอกคุณตอนที่คุณหยิบ WebSocket มาใช้ครั้งแรก: API มอบ send(data) และ onmessage(event) ให้คุณ และนั่นคือ ทั้งหมด connection เป็นเหมือน ท่อทึบ (opaque pipe) จะส่ง string 'hi' ก็ได้ JSON object ที่แปลงเป็น string แล้วก็ได้ หรือ binary สักก้อนก็ได้ — ท่อไม่มีความเห็นกับข้อมูลเหล่านั้นเลย ไม่มีแนวคิดสำเร็จรูปของ “chat message”, “request”, “error” หรือ “version”
ความว่างเปล่านั้นคือฟีเจอร์ ไม่ใช่ความบกพร่อง HTTP ฝัง headers, status codes, methods และการต่อรอง content ไว้ในตัว ส่วน WebSocket แทบไม่ฝังอะไรเลยเพื่อให้ คุณ ออกแบบบทสนทนาได้ตรงตามที่แอปพลิเคชันต้องการ ด้านกลับกันคือความรับผิดชอบ: message ของคุณจะมีโครงสร้างแบบไหน ก็เพราะคุณเป็นคนใส่ลงไปเอง
flowchart TB
subgraph app["Your application"]
a1["chat lines · presence · edits · requests"]
end
subgraph proto["Your message protocol — THIS module"]
p1["type · payload · id · version · schema"]
end
subgraph ws["WebSocket transport"]
w1["send(text or binary) · onmessage"]
end
app --> proto --> ws
ws -- "opaque bytes" --> ws ชั้นตรงกลางคือชั้นที่ไม่มีค่าเริ่มต้นมาให้เลย ออกแบบชั้นนี้ให้ดีคือเส้นแบ่งระหว่าง protocol ที่ต่อยอดไปได้เป็นปี ๆ กับ protocol ที่กลายเป็นกองพันกันของ string ซึ่งต้องเขียนเงื่อนไขพิเศษเฉพาะกรณีไปเรื่อย ๆ
โมดูลนี้ครอบคลุมอะไรบ้าง
หัวข้อที่มีชื่อว่า “โมดูลนี้ครอบคลุมอะไรบ้าง”ห้าบทเรียน แต่ละบทเพิ่มหนึ่งแนวคิดสำคัญเข้าสู่ชุดเครื่องมือการออกแบบ message ของคุณ:
- การออกแบบ Message (คุณอยู่ตรงนี้) — ทำไม socket จึงว่างเปล่าและคุณคือคนใส่ความหมายลงไป
- Framing messages — WebSocket message นั้นแยกเป็นชิ้น ๆ อยู่แล้ว แต่คุณยังต้องมี protocol ระดับ application อยู่ดี หนึ่ง JSON object ต่อหนึ่ง message
- JSON vs binary — JSON ที่อ่านได้และใช้ได้ทุกที่ เทียบกับ format แบบ binary ที่กระชับอย่าง MessagePack, Protobuf และ CBOR และเมื่อไรแต่ละแบบจึงชนะ
- Request/response over WebSocket — socket นั้นเน้น message ไม่ใช่ request/reply คุณจึงเพิ่ม correlation
idเพื่อจับคู่คำตอบกลับไปยังคำถามต้นทาง - Schemas และ versioning — message envelope การ validate สิ่งที่เข้ามา และการพัฒนา protocol โดยไม่ทำให้ client รุ่นเก่าพัง
message ที่มีโครงสร้างชิ้นแรก
หัวข้อที่มีชื่อว่า “message ที่มีโครงสร้างชิ้นแรก”ด้านล่างคือก้าวที่เล็กที่สุดที่ขยับขึ้นมาจากการส่ง text เปล่า ๆ: การส่ง JSON object แล้ว parse ที่อีกฝั่ง โค้ดชุดนี้รันในเบราว์เซอร์ของคุณกับ echo socket ภายในหน้าที่ใช้ WebSocket API จริง — onopen, onmessage, send, close แบบเดียวกัน — จึงไม่ต้องสตาร์ท server ใด ๆ ในแอปจริง มีเพียงบรรทัดแรกเท่านั้นที่เปลี่ยนเป็น new WebSocket('wss://your-server')
สังเกตว่าทันทีที่เราส่ง object แทนที่จะเป็น string เปล่า ๆ ฝั่งรับสามารถถามคำถามที่มีความหมายได้: message นี้ ชนิดอะไร? ใคร เป็นคนส่ง? เมื่อไร?
socket รับส่ง text ทั้งสองทิศทางโดยไม่เคยสนใจเลยสักครั้งว่าข้างในเป็น JSON โครงสร้าง — type, from, text — มีอยู่ได้ก็เพราะ เรา ตกลงกันแล้วใส่ลงไปเอง ทุกบทเรียนที่ตามมาคือการขัดเกลาข้อตกลงนั้นให้ดียิ่งขึ้น