ข้ามไปยังเนื้อหา
chaninl
en

Build Log

ลูกค้าขอแค่เว็บแสดงสินค้า — แต่ข้อมูลจริงอยู่ใน POS อายุ 30 ปี

โดย chaninl ·

โจทย์ตอนแรกฟังดูง่ายมากครับ — เว็บที่แสดงสินค้าของลูกค้า แบบที่นั่งประชุมรอบแรก แล้วพยักหน้าตามได้เลย เริ่มสเก็ตช์หน้าเว็บในหัวไปแล้วด้วยซ้ำ

แล้วจุดหักมุมก็มา ข้อมูลสินค้าทั้งหมดอยู่บนคอมพิวเตอร์ที่ออฟฟิศของลูกค้า มันถูก generate จาก script ที่ไปดึงข้อมูลจากระบบ Visual FoxPro — ใช่ครับ FoxPro — ทุกๆ 1 ชั่วโมง และผมจะไม่มีทางได้แตะ source system นั้นเลย ผมอ่านได้แค่สิ่งที่มัน ปล่อยออกมา เท่านั้น

เพราะงั้นงานนี้ไม่เคยเป็นงานเว็บเลยครับ มันคืองาน integration ที่ใส่ชุดเว็บมาหลอกเรา

ข้อจำกัดที่เราไม่ได้เป็นเจ้าของ

ก่อนผมจะเขียนโค้ดสักบรรทัด รูปร่างจริงของโปรเจกต์ถูกกำหนดโดยสิ่งที่ผมคุมไม่ได้เลย:

  • ข้อมูลอยู่ on-prem — มันอยู่บนเครื่องที่ออฟฟิศลูกค้า ระบบของผมเลยต้องไปรันที่นั่น ด้วย ไม่ใช่บน cloud สวยๆ ที่ผมจัดการเองได้
  • source ไม่มี API — Visual FoxPro ไม่ได้ยื่น endpoint สวยๆ ให้ สัญญาเดียวที่ผมได้ คือไฟล์ที่ script ตัวนั้นปล่อยออกมา
  • ข้อมูลที่ลูกค้าให้ไม่เคยครบ — เขาบอกว่าข้อมูลรีเฟรชทุก 1 ชั่วโมง จริงๆ คือ 2 ชั่วโมง รายละเอียดเล็กๆ แต่บทเรียนใหญ่ เดี๋ยวผมจะวนกลับมาเล่า

เมื่อเราเปลี่ยน source ไม่ได้ ข้อจำกัดพวกนี้ คือ ตัว design เลยครับ หน้าที่ผมคือ ออกแบบรอบมัน ไม่ใช่หวังให้มันหายไป

Architecture v1

สุดท้ายมันออกมาแบบนี้ครับ ทุกอย่างด้านล่างรันอยู่บนคอมที่ออฟฟิศลูกค้า ผ่าน Docker และออกสู่โลกภายนอกผ่าน tunnel เส้นเดียว:

คอมที่ออฟฟิศลูกค้า  (Windows + Docker)
┌────────────────────────────────────────────────┐
│  Visual FoxPro ──script──▶ CSV files           │
│                               │                │
│                          file watcher          │
│                               │                │
│                             ETL ──▶ database   │
│                                       │        │
│                                  API server    │
└────────────────────────────────────────┬───────┘
                                         │  Cloudflare Tunnel

                                  Next.js (server-side)


                                     ผู้เข้าชมเว็บ

แต่ละชิ้นมีอยู่เพราะข้อจำกัด ไม่ใช่เพราะว่ามันน่าสนใจที่จะใส่เข้ามา:

  • file watcher → ETL → database เพราะผมอ่านได้แค่ไฟล์ ผมเลยคอย watch ไฟล์ใหม่ที่ ถูกปล่อยมา แปลงข้อมูล แล้วเก็บเป็นก๊อปปี้ของผมเองที่ query ได้
  • API server บนเครื่องลูกค้า เพราะข้อมูลมันอยู่ตรงนั้น บวกกับการ์ดมาตรฐานที่ฝั่ง API: API key, CORS, rate limit
  • Cloudflare Tunnel เพื่อพา API ที่อยู่ on-prem ออกอินเทอร์เน็ตให้เว็บเข้าถึงได้ ส่วนนี้ ตั้งค่าง่ายจนน่าตกใจ — เป็นชิ้นที่ราบรื่นที่สุดของทั้ง stack เลยครับ
  • Next.js สำหรับหน้าบ้าน เพราะผมต้องการให้ส่วนที่เป็นข้อมูล sensitive อยู่ฝั่ง server-side ไม่ให้มันรั่วผ่าน client-side API call

ระหว่างที่คิดเรื่องพวกนี้ ผมใช้ AI เป็นคู่คิดในการออกแบบไปด้วย — ไม่ใช่แค่ให้มันเขียนโค้ด แต่ให้ช่วยเถียง trade-off ออกมาดังๆ และผมยังเก็บ Architecture Decision Record (ADR) ไว้ใน repo ที่เขียนโค้ดเลย เพื่อให้ เหตุผล เบื้องหลังแต่ละการตัดสินใจอยู่ติดกับโค้ด ของแถมที่ไม่ได้คาดไว้คือ มันกลายเป็น context ที่ AI agent กลับมาอ่านย้อนได้ — เดี๋ยวจะ เล่าวิธีนี้ลึกๆ ในโพสต์ถัดไปครับ

ตอนที่โลกแห่งความจริงโต้กลับ

มีสองช่วงที่ผมอยากเก็บไว้เล่า

“ทุก 1 ชั่วโมง” ที่จริงคือทุก 2 ชั่วโมง เพราะผมทำ refresh interval เป็นค่า config มาตั้งแต่วันแรก ไม่ได้ฝังเป็นตัวเลขไว้ลึกๆ ในโค้ด เรื่องนี้เลยเป็นการแก้บรรทัดเดียว ไม่ใช่ การไล่หา นี่แหละบทเรียนทั้งหมด: สมมติฐานของลูกค้าจะผิด เพราะงั้นทำทุกสมมติฐานให้เป็นตัวแปร

การเปิด Docker บน Windows ต้องไปแก้ BIOS ผมพยายามอย่างหนักให้งานนี้เป็นงาน remote ไม่ต้อง onsite แต่ virtualization ถูกปิดไว้ที่ firmware แผนนั้นเลยพังและผมต้องหาวิธีการใหม่

บทเรียนเดียวที่อยากให้จำ

ก่อนเขียนโค้ดบรรทัดแรก ให้ลิสต์ทุกอย่างที่เรา ไม่ได้ เป็นเจ้าของ — source system, ที่อยู่ของข้อมูล, สิ่งที่ลูกค้ายังไม่ได้บอก ลิสต์นั้นแหละคือสิ่งที่กำหนด architecture จริงๆ ที่เหลือเป็นแค่ความชอบส่วนตัว

ตอนหน้า

ไฟล์ที่ออกมาจาก FoxPro กลายเป็น “สัญญา” ที่ไม่มีเอกสารเลยครับ มันครอบข้อความด้วย " แต่ไม่ escape เครื่องหมาย inch-mark ที่อยู่ข้างใน — CSV parser ปกติเลยพังเงียบๆ การ reverse-engineer ฟอร์แมตนี้คือเรื่องของโพสต์ถัดไป

← กลับไปที่บล็อก