PlusMagi's Blog By Pitt Phunsanit Backend UML: Dependency ความสัมพันธ์แบบอิงอาศัยกัน หากอีกฝั่งเปลี่ยนแปลงจะมีผลกระทบ

UML: Dependency ความสัมพันธ์แบบอิงอาศัยกัน หากอีกฝั่งเปลี่ยนแปลงจะมีผลกระทบ

💡 UML Dependency: ความสัมพันธ์ที่เปราะบางและผลกระทบจากการเปลี่ยนแปลง

ในฐานะ System Architect การทำความเข้าใจรูปแบบความสัมพันธ์ระหว่างองค์ประกอบ (Elements) ในการออกแบบซอฟต์แวร์เป็นสิ่งสำคัญอย่างยิ่ง UML Dependency หรือ ‘การพึ่งพาทางโครงสร้าง’ คือความสัมพันธ์ที่ระบุว่าคลาสหรือโมดูลหนึ่งๆ จำเป็นต้องรู้ว่าอีกคลาสหนึ่งมีอยู่และทำงานอย่างไร โดยที่ความสัมพันธ์นี้ไม่ได้บ่งบอกถึงการเป็นเจ้าของ (Ownership) หรือการสืบทอด (Inheritance) อย่างชัดเจน

พูดง่ายๆ คือ Dependency คือการที่ Component A มีการอ้างอิงถึง Type หรือ API ของ Component B เมื่อเกิดการเปลี่ยนแปลงใน Component B ไม่ว่าจะเป็นการเปลี่ยนชื่อเมธอด การแก้ไขพารามิเตอร์ หรือแม้กระทั่งการลบคลาสต้นฉบับ (Source) มันจะส่งผลกระทบโดยตรงและทำให้ Component A หยุดทำงาน นี่คือลักษณะที่ทำให้ Dependency มีความเสี่ยงสูง (High Fragility).

🔍 ความเสี่ยงของ Dependency ในมุมมองของสถาปัตยกรรม

Dependency ที่มากเกินไป (High Coupling) ทำให้ระบบมีความเปราะบางสูง (Fragile System) เพราะการเปลี่ยนแปลงเล็กน้อยในส่วนใดส่วนหนึ่งอาจก่อให้เกิดผลกระทบแบบโดมิโน (Domino Effect) ทั่วทั้งระบบ การจัดการ Dependency จึงเป็นหัวใจสำคัญของการออกแบบที่ยืดหยุ่น (Maintainable) และสามารถขยายได้ (Scalable)

🔗 การจำลอง Dependency ในระบบฐานข้อมูล (PostgreSQL Context)

แม้ว่า Dependency จะเป็นแนวคิดเชิง Object-Oriented แต่เราสามารถเห็นภาพความเปราะบางนี้ได้ในระดับโครงสร้างข้อมูล (Schema) ของ PostgreSQL เมื่อโค้ดแอปพลิเคชันของเราถูกเขียนขึ้นโดยอ้างอิงถึงชื่อคอลัมน์หรือประเภทข้อมูลที่เฉพาะเจาะจง (Hardcoding) หาก Database Administrator (DBA) ทำการแก้ไข Schema โดยไม่แจ้งให้ผู้พัฒนาทราบ โค้ดส่วนที่เรียกใช้ก็จะล้มเหลวทันที นี่คือตัวอย่างที่แสดงให้เห็นถึงการพึ่งพาทางโครงสร้าง:

SQL

-- 1. สถานะเริ่มต้น: แอปพลิเคชันทำงานได้ปกติ (สมมติว่าใช้คอลัมน์ 'user_id')
SELECT product_name, user_id FROM sales_data WHERE user_id = '1';

สมมติว่าโค้ดแอปพลิเคชันส่วนหนึ่งถูกสร้างขึ้นมาเพื่อดึงข้อมูลโดยอ้างอิงถึงคอลัมน์ `user_id` นี้อย่างเฉพาะเจาะจง

⚠️ การเปลี่ยนแปลง (The Dependency Break): หาก DBA ตัดสินใจเปลี่ยนชื่อคอลัมน์จาก `user_id` เป็น `account_id` โดยไม่มีการอัปเดตโค้ดแอปพลิเคชัน

SQL

-- 2. สถานะหลังการเปลี่ยนแปลง: โค้ดที่เคยทำงานจะเกิดข้อผิดพลาด (Column not found)
SELECT product_name, user_id FROM sales_data WHERE user_id = '1';

ระบบจะเกิดข้อผิดพลาด (Error) ทันที แม้ว่าข้อมูลจะยังอยู่ครบถ้วนก็ตาม เพราะโค้ดของเราพึ่งพาชื่อคอลัมน์เดิม

🛡️ แนวทางการลด Dependency และเพิ่มความยืดหยุ่น

ในฐานะ Senior Architect เราต้องใช้หลักการออกแบบเพื่อลดการพึ่งพาที่แน่นหนา (Tight Coupling) โดยการใช้เทคนิคต่างๆ เช่น:

  1. Abstraction: การใช้ Interface หรือ Abstract Class เพื่อกำหนดสัญญา (Contract) ของการทำงาน ทำให้โค้ดเรียกใช้เพียงสัญญา ไม่ใช่การใช้งานจริงของคลาส
  2. Dependency Injection (DI): แทนที่จะให้ Component A สร้าง Component B เอง ควรส่ง Component B เข้ามาให้ Component A ผ่าน Constructor หรือ Setter ทำให้สามารถ Mock หรือเปลี่ยน Implementation ได้ง่าย
  3. API Gateway/Service Layer: ในระดับ Microservices ควรมี Service Layer ที่ทำหน้าที่เป็นตัวกลางในการสื่อสารข้อมูล เพื่อป้องกันไม่ให้ Service A ต้องรู้รายละเอียดภายในของ Service B โดยตรง

✅ บทสรุป

การทำความเข้าใจ UML Dependency ไม่ใช่แค่การรู้สัญลักษณ์ในแผนภาพ แต่คือการตระหนักถึงความเปราะบางที่ซ่อนอยู่ในโค้ดของเรา การออกแบบที่ดีคือการสร้างระบบที่สามารถรับมือกับการเปลี่ยนแปลงได้โดยที่ส่วนประกอบต่างๆ ไม่ล่มสลายตามไปด้วย การใช้หลักการ Decoupling อย่างสม่ำเสมอจะช่วยให้เราสร้างระบบที่แข็งแกร่งและบำรุงรักษาได้ในระยะยาว