เอกสารอ้างอิงการใช้งาน นี่คือสถาปัตยกรรมเชิงตัวอย่างและเช็กลิสต์ขอบเขตงาน ไม่ใช่โครงการลูกค้าที่ผ่านการตรวจสอบหรือการติดตั้งใช้งานที่มีผลการวัดจริง เทคโนโลยี ขอบเขตการผสานระบบ และรายการส่งมอบจะตกลงกันตามบรีฟจริง
สถานการณ์และข้อจำกัด
ผลิตภัณฑ์ที่ให้บริการหลายองค์กรลูกค้าจากการติดตั้งใช้งานเพียงชุดเดียว การตัดสินใจที่ส่งผลต่อทุกอย่างคือรูปแบบการแยกข้อมูล ได้แก่ ตารางร่วมที่มีคอลัมน์ tenant, schema แยกตาม tenant หรือฐานข้อมูลแยกตาม tenant การเลือกค่าเริ่มต้นโดยไม่พิจารณาอย่างรอบคอบเป็นข้อผิดพลาดที่มีต้นทุนสูงที่สุดในหมวดนี้ เพราะการเปลี่ยนภายหลังหมายถึงการย้ายข้อมูลลูกค้าที่กำลังใช้งานจริง
สถาปัตยกรรม
เลเยอร์และความรับผิดชอบ
| เลเยอร์ | เทคโนโลยี | ความรับผิดชอบ |
|---|---|---|
| เว็บ | Next.js (SSR สำหรับการตลาดและ SEO, SPA หลังเข้าสู่ระบบ) | หน้าสาธารณะใช้การเรนเดอร์ฝั่งเซิร์ฟเวอร์เพื่อการค้นหา ส่วนผลิตภัณฑ์ที่ยืนยันตัวตนแล้วไม่จำเป็นต้องใช้และทำงานเป็นแอปฝั่งไคลเอนต์ |
| API | Go + Gin, ระบุ tenant ในแต่ละคำขอ | ระบุบริบท tenant จาก subdomain หรือ token ก่อน handler ใด ๆ ทำงาน แล้วส่งต่อบริบทดังกล่าวไปกับทุก query |
| พื้นที่จัดเก็บ | PostgreSQL, schema ร่วมพร้อมการกำหนดขอบเขต tenant ระดับแถว | ฐานข้อมูลเดียวที่ทุกตารางของ tenant มี tenant_id บังคับใช้ที่ชั้น repository และหากรองรับ ให้ใช้ row-level security เป็นแนวป้องกันอีกชั้น |
| แคช | Redis, คีย์มีคำนำหน้า tenant | เซสชัน rate limit และการตั้งค่าที่เรียกใช้บ่อย การเติมคำนำหน้าคีย์ช่วยป้องกันการชนกันของ cache key ซึ่งอาจทำให้ข้อมูลของ tenant หนึ่งรั่วไหลไปยังอีก tenant |
| การเรียกเก็บเงิน | Stripe หรือเกตเวย์ระดับภูมิภาค | ทำสำเนาสถานะ subscription ไว้ในระบบภายใน เพื่อให้การตรวจสิทธิ์การใช้งานไม่ต้องพึ่งการเรียกแบบสดไปยังผู้ให้บริการชำระเงิน |
| งานเบื้องหลัง | คิวเวิร์กเกอร์พร้อมบริบท tenant | งานตามกำหนดเวลาและงานอะซิงโครนัส โดยบริบท tenant เดินทางไปพร้อม payload ของงาน แทนการอนุมานขณะเริ่มทำงาน |
โมเดลข้อมูล
ตารางด้านล่างเป็นที่เก็บกฎซึ่งส่วนอื่นทั้งหมดต้องพึ่งพา หากออกแบบผิด สิ่งที่เริ่มจากเดโมที่ใช้งานได้จะกลายเป็นระบบที่ต่อยอดไม่ได้
เอนทิตีหลัก
| เอนทิตี | ฟิลด์หลัก ความสัมพันธ์ และกฎ |
|---|---|
| tenant | ระเบียนองค์กรที่มีแผน สถานะ ภูมิภาค และ feature flags ทุกตารางที่เป็นของ tenant ต้องมี tenant_id และห้าม query ใดละเว้นฟิลด์นี้ |
| user / membership | ผู้ใช้หนึ่งคนอาจอยู่ในหลาย tenant และมีบทบาทต่างกันในแต่ละแห่ง ดังนั้นตัวตนจึงเป็นระดับส่วนกลาง ขณะที่สิทธิ์เป็นระดับ membership การรวม user และ membership เป็นตารางเดียวจะทำให้การรองรับหลายองค์กรในภายหลังเป็นไปไม่ได้ |
| role / permission | สิทธิ์มอบให้ role, role กำหนดต่อ membership และทุกการตรวจสอบทำใน backend การซ่อนใน frontend เป็นเพียงการนำเสนอ ไม่ใช่การบังคับใช้ |
| subscription / entitlement | แผน จำนวนที่นั่ง รอบระยะเวลา และสิทธิ์ที่อนุมานได้ การเข้าถึงฟีเจอร์อ่านจาก entitlement ภายในระบบ ดังนั้นผู้ให้บริการชำระเงินขัดข้องจึงไม่ทำให้ลูกค้าถูกล็อกออกจากผลิตภัณฑ์ |
| audit_log | ผู้ดำเนินการ tenant การกระทำ เป้าหมาย และ payload ก่อน/หลัง รองรับการทบทวนด้านความปลอดภัยและการวิเคราะห์เหตุการณ์ ควรตกลงระยะเวลาเก็บรักษา สิทธิ์เข้าถึง และข้อกำหนดการตรวจสอบก่อนเริ่มพัฒนา |
| tenant_config | การตั้งค่า แบรนด์ และการแทนค่าฟีเจอร์แยกตาม tenant โดยแก้ไขตามลำดับความสำคัญที่จัดทำเป็นเอกสารไว้เหนือค่าเริ่มต้นของแผน |
อินเทอร์เฟซหลัก
Endpoint ที่มีกฎทางธุรกิจ
| Endpoint | วัตถุประสงค์และข้อสังเกตด้านการทำงานพร้อมกัน |
|---|---|
| POST /api/v1/auth/login | ยืนยันตัวตน แล้วส่งคืน membership ของ tenant ที่มีอยู่ โดยเลือก tenant อย่างชัดเจน แทนการเดาจากเซสชันล่าสุด |
| GET /api/v1/{resource} | การอ่านข้อมูลที่มีขอบเขต tenant ทุกครั้งต้องผ่าน repository ที่เติม tenant_id ให้เอง การกำหนดขอบเขตไว้ที่เดียวทำให้ handler ไม่สามารถลืมได้ ซึ่งเป็นสาเหตุที่พบบ่อยที่สุดของข้อมูลรั่วไหลข้าม tenant |
| POST /api/v1/webhooks/billing | Webhook จากผู้ให้บริการชำระเงิน ทำงานแบบ idempotent ตาม event id และอัปเดตสถานะ subscription กับ entitlement ภายในระบบ |
| POST /api/v1/tenants/{id}/invite | กระบวนการเชิญที่สร้าง membership พร้อม role และจำกัดอัตราต่อ tenant เพื่อป้องกันการใช้งานระบบเชิญในทางที่ผิด |
| GET /api/v1/audit | Query audit ที่มีขอบเขต tenant พร้อม pagination เปิดให้ผู้ดูแล tenant ใช้ตอบข้อกำหนดด้าน compliance ด้วยตนเอง |
การตัดสินใจทางเทคนิค
- ใช้ schema ร่วมกับ tenant_id โดยผ่านการเลือกอย่างตั้งใจ มีต้นทุนการดำเนินงานและการย้ายข้อมูลต่ำที่สุด และเหมาะกับผลิตภัณฑ์ส่วนใหญ่ หากสัญญาลูกค้ากำหนดให้แยกทางกายภาพ ฐานโค้ดเดียวกันสามารถทำงานบนการติดตั้งเฉพาะได้ โดยออกแบบเส้นทางนี้ไว้ตั้งแต่ต้น ไม่ใช่เพิ่งพบระหว่างการเจรจาขาย
- บังคับใช้ tenant scoping ที่ repository layer เดียว การพึ่งพาให้นักพัฒนาทุกคนจำต้องใส่ WHERE clause คือสาเหตุของข้อมูลรั่วไหลข้าม tenant การรวมศูนย์ทำให้เขียน query ที่ไม่ปลอดภัยโดยบังเอิญได้ยาก
- ทำสำเนา entitlement ไว้ในระบบภายใน การตรวจผู้ให้บริการชำระเงินทุกคำขอเพิ่ม latency และ dependency ที่เข้มงวด การทำสำเนาภายในทำให้ผู้ให้บริการขัดข้องกระทบการดำเนินงานด้าน billing ไม่ใช่ตัวผลิตภัณฑ์เอง
- มี audit log ตั้งแต่รุ่นแรก ไม่สามารถเติมประวัติ audit ย้อนหลังได้ ควรยืนยันข้อกำหนด audit ของผู้ซื้อและเก็บประวัติที่ตกลงกันตั้งแต่รุ่นแรก
- ใช้ SSR เฉพาะหน้าสาธารณะ หน้าการตลาดและเอกสารต้องการการมองเห็นในระบบค้นหา ส่วนผลิตภัณฑ์ที่ยืนยันตัวตนแล้วไม่ต้องการ และการบังคับใช้ SSR จะเพิ่มต้นทุนโดยไม่มีผลตอบแทน
เป้าหมายการออกแบบเชิงสมมติ
ตัวเลขด้านล่างเป็นตัวอย่างการวางแผนความจุเชิงสมมติ ไม่ใช่ผลลัพธ์ลูกค้าที่วัดจริง คำแนะนำ sizing ที่ผ่านการตรวจสอบ หรือ SLA กำหนดเป้าหมายการยอมรับเฉพาะโครงการหลังระบุ workload อุปกรณ์ ข้อมูล ฮาร์ดแวร์ และ dependency จากบุคคลที่สามแล้ว และตรวจสอบด้วย pilot กับ load test ก่อนผูกพันกับเป้าหมาย production
เป้าหมายด้านความจุและประสิทธิภาพ
| เป้าหมาย | ค่าและสมมติฐาน |
|---|---|
| API latency | p95 ต่ำกว่า 250 ms สำหรับการอ่านที่มีขอบเขต tenant โดยมี composite index ที่ถูกต้องและเริ่มด้วย tenant_id |
| การแยก tenant | ต้องมีชุดทดสอบอัตโนมัติที่พยายามเข้าถึงข้อมูลข้าม tenant ในทุก endpoint และทำให้ build ล้มเหลวเมื่อพบการเข้าถึงที่ไม่ได้รับอนุญาต การติดตั้งจริงต้องผ่านการตรวจเหล่านี้ เอกสารอ้างอิงนี้ไม่ใช่หลักฐานว่าการทดสอบความปลอดภัยเสร็จสมบูรณ์แล้ว |
| การเริ่มใช้งาน | จัดเตรียม tenant ใหม่ได้ภายในไม่กี่วินาที เนื่องจากเป็นการสร้าง row และระเบียนการตั้งค่า ไม่ใช่งานโครงสร้างพื้นฐาน |
| การขยายขนาด | รองรับ tenant ระดับหลักพันต้น ๆ บน PostgreSQL instance เดียว เมื่อเกินกว่านั้นให้ใช้ read replica แล้วจึงแบ่ง shard ตาม tenant โดยวางแผนไว้แต่ไม่สร้างก่อนจำเป็น |
สิ่งที่รวมในการส่งมอบ
- ซอร์ส frontend ของ Next.js, ซอร์ส backend ของ Go และซอร์สแผงผู้ดูแลใน repository ของคุณ
- สคีมาฐานข้อมูลพร้อม tenant scoping, index และตาราง audit ที่จัดทำเป็นเอกสาร รวมถึงประวัติ migration
- ชุดทดสอบการแยกข้อมูลข้าม tenant ที่รันได้ใน CI ของคุณ
- การผสานระบบ billing, การจัดการ webhook และเอกสาร entitlement
- บันทึกการติดตั้งใช้งานสำหรับ Docker และ Nginx, การตั้งค่าสภาพแวดล้อม, ขั้นตอนสำรองและกู้คืนข้อมูล
การวางแผนให้เหมาะกับธุรกิจของคุณ
โมดูลด้านบนเป็นจุดเริ่มต้น ไม่ใช่แพ็กเกจตายตัว โดยปกติเราจะจำกัดรุ่นแรกไว้ที่ workflow หลักหนึ่งรายการและ workflow ผู้ดูแลหนึ่งรายการ จากนั้นจึงขยายเมื่อการใช้งานจริงแสดงให้เห็นว่าภาระงานและกรณีขอบอยู่ที่ใด ส่งบรีฟที่กำหนดขอบเขตแล้ว และเราจะเทียบกับสถาปัตยกรรมนี้ พร้อมแจ้งว่าส่วนใดที่คุณยังไม่จำเป็นต้องทำ
