Published on

การแยกข้อมูลระหว่างโมดูล (Data Isolation)

Authors

การแยกข้อมูลระหว่างโมดูล (Data Isolation)

ในสถาปัตยกรรมแบบ Modular Monolith ที่ได้รับความนิยมเพิ่มขึ้นเรื่อยๆ ซึ่งเป็นการผสมผสานข้อดีของ Monolith และ Microservices เข้าไว้ด้วยกัน แนวทางนี้ช่วยแก้ปัญหาเรื่องการผูกแน่นของส่วนประกอบต่างๆ (tight coupling) ที่มักพบในสถาปัตยกรรมแบบ Monolith ดั้งเดิม โดยบังคับใช้แนวทางปฏิบัติทางสถาปัตยกรรมที่ดีขึ้นด้วยการกำหนดขอบเขตโมดูลและรูปแบบการสื่อสารที่ชัดเจน

อย่างไรก็ตาม สิ่งหนึ่งที่มองข้ามไม่ได้คือ การแยกข้อมูลระหว่างโมดูล (Data Isolation) ซึ่งช่วยให้โมดูลมีความเป็นอิสระและมีการผูกแน่นที่หลวม (loosely coupled) สถาปัตยกรรมแบบ Modular Monolith มีกฎที่เข้มงวดสำหรับการรักษาความสมบูรณ์ของข้อมูล:

  • แต่ละโมดูลสามารถเข้าถึงได้เฉพาะตารางของตนเองเท่านั้น
  • ไม่มีการแชร์ตารางหรืออ็อบเจกต์ระหว่างโมดูล
  • การ Join สามารถทำได้เฉพาะตารางภายในโมดูลเดียวกันเท่านั้น

ประโยชน์ของการออกแบบนี้คือการส่งเสริมความเป็นโมดูลาร์และการผูกแน่นที่หลวม ทำให้ง่ายต่อการเปลี่ยนแปลงระบบและลดผลข้างเคียงที่ไม่พึงประสงค์


ระดับการแยกข้อมูล (Data Isolation Levels)

นี่คือสี่แนวทางในการแยกข้อมูลสำหรับ Modular Monoliths:

Level 1 - Separate Table (ไม่แนะนำ)

เป็นวิธีการที่ง่ายที่สุดคือไม่มีการแยกข้อมูลในระดับฐานข้อมูล ทุกตารางของทุกโมดูลจะอยู่ในฐานข้อมูลเดียวกัน ทำให้ยากที่จะระบุว่าตารางใดเป็นของโมดูลใด แม้จะใช้ได้กับแอปพลิเคชันขนาดเล็ก แต่ยิ่งมีตารางมากเท่าไหร่ก็ยิ่งรักษาสภาพการแยกข้อมูลได้ยากขึ้นเท่านั้น

┌─────────────────────────────────┐
│         Single Database         │
│ ┌─────────────────────────────┐ │
│ │          Module A           │ │
│ │  Table A1  Table A2  ...    │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │          Module B           │ │
│ │  Table B1  Table B2  ...    │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │          Module C           │ │
│ │  Table C1  Table C2  ...    │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘

Level 2 - Separate Schema (Logical Isolation)

วิธีนี้เป็นการจัดกลุ่มตารางที่เกี่ยวข้องเข้าด้วยกันในฐานข้อมูลโดยใช้ Database Schemas แต่ละโมดูลมี Schema เฉพาะของตนเองที่มีตารางของโมดูลนั้นๆ ทำให้ง่ายต่อการจำแนกว่าตารางใดเป็นของโมดูลใด คุณสามารถกำหนดกฎเพื่อป้องกันการสืบค้นข้อมูลจากโมดูลอื่นได้ เช่น การใช้ Architecture Tests เป็นจุดเริ่มต้นที่ดีในการสร้าง Modular Monolith

┌─────────────────────────────────┐
│         Single Database         │
│ ┌─────────────────────────────┐ │
│ │         Schema A            │ │
│ │  Table A1  Table A2  ...    │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │         Schema B            │ │
│ │  Table B1  Table B2  ...    │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │         Schema C            │ │
│ │  Table C1  Table C2  ...    │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘

Level 3 - Separate Database (Physical Isolation)

ระดับถัดไปคือการย้ายข้อมูลของแต่ละโมดูลไปยัง ฐานข้อมูลที่แยกจากกัน วิธีนี้มีการจำกัดมากกว่าการแยกข้อมูลด้วย Schema และเป็นทางเลือกที่ดีหากคุณต้องการกฎการแยกข้อมูลที่เข้มงวดระหว่างโมดูล แม้จะมีข้อเสียคือความซับซ้อนในการจัดการโครงสร้างพื้นฐานสำหรับฐานข้อมูลหลายตัว แต่ก็เป็นขั้นตอนที่ยอดเยี่ยมในการแยกโมดูลออกไปเป็น Microservices ในอนาคต

┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│  Database A     │   │  Database B     │   │  Database C     │
│ ┌─────────────┐ │   │ ┌─────────────┐ │   │ ┌─────────────┐ │
│ │ Module A    │ │   │ │ Module B    │ │   │ │ Module C    │ │
│ │ Table A1    │ │   │ │ Table B1    │ │   │ │ Table C1    │ │
│ │ Table A2    │ │   │ │ Table B2    │ │   │ │ Table C2    │ │
│ │     ...     │ │   │ │     ...     │ │   │ │     ...     │ │
│ └─────────────┘ │   │ └─────────────┘ │   │ └─────────────┘ │
└─────────────────┘   └─────────────────┘   └─────────────────┘

Level 4 - Different Persistence (Polyglot Persistence)

วิธีนี้ไปไกลกว่านั้นโดยการใช้ ฐานข้อมูลประเภทที่แตกต่างกัน สำหรับแต่ละโมดูล ตัวอย่างเช่น โมดูลหนึ่งอาจใช้ฐานข้อมูลเชิงสัมพันธ์ (SQL) ในขณะที่อีกโมดูลหนึ่งอาจใช้ฐานข้อมูลเอกสาร (Document Database) หรือฐานข้อมูลกราฟ (Graph Database) เพื่อแก้ปัญหาเฉพาะทาง การเลือกใช้ Persistence Model ที่แตกต่างกันนี้ต้องมีการวางแผนอย่างรอบคอบ แต่สามารถเป็น Trade-off ที่คุ้มค่าสำหรับ Use Case ของคุณ

┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│  Database A     │   │  Database B     │   │  Database C     │
(Relational DB) │   │ (Document DB)   │   │ (Graph DB)│ ┌─────────────┐ │   │ ┌─────────────┐ │   │ ┌─────────────┐ │
│ │ Module A    │ │   │ │ Module B    │ │   │ │ Module C    │ │
│ │ Table A1    │ │   │ │ Doc B1      │ │   │ │ Node C1     │ │
│ │ Table A2    │ │   │ │ Doc B2      │ │   │ │ Edge C1     │ │
│ │     ...     │ │   │ │     ...     │ │   │ │     ...     │ │
│ └─────────────┘ │   │ └─────────────┘ │   │ └─────────────┘ │
└─────────────────┘   └─────────────────┘   └─────────────────┘

สรุป

Modular Monoliths เป็นตัวเลือกที่ยอดเยี่ยมหากคุณยังไม่ต้องการ Microservices ทันที คุณสามารถพัฒนาแอปพลิเคชันของคุณเป็น Monolith ที่มีขอบเขตที่ชัดเจนภายในระบบ และยังคงมีความยืดหยุ่นในการแยกโมดูลและย้ายไปยัง Microservices ได้ในอนาคต โดยที่ยังคงความเร็วในการพัฒนาที่เร็วกว่า Modular Monolith กำหนดให้โมดูลต่างๆ ปฏิบัติตามกฎบางอย่าง เช่น การเข้าถึงเฉพาะตารางของตนเอง ไม่แชร์ตารางกับโมดูลอื่น และไม่สืบค้นตารางของโมดูลอื่นโดยตรง กฎเหล่านี้ช่วยบังคับใช้การแยกข้อมูลระหว่างโมดูล

โดยทั่วไปแล้ว การเริ่มต้นด้วยการแยกข้อมูลแบบ Logical Isolation โดยใช้ Schemas เป็นวิธีที่ง่ายต่อการนำไปใช้และช่วยให้เข้าใจขอบเขตของระบบได้ดีขึ้น และคุณสามารถพิจารณาใช้ฐานข้อมูลแยกกัน (Separate Database) ในภายหลังได้ตามความต้องการของระบบที่เปลี่ยนแปลงไป