PlusMagi's Blog By Pitt Phunsanit Backend,Database Clean Architecture: Database Gateways & Inversion

Clean Architecture: Database Gateways & Inversion

ในฐานะ Senior Architect ที่เชี่ยวชาญด้านระบบฐานข้อมูล เราทราบดีว่าการออกแบบระบบที่ยั่งยืนต้องอาศัยการแยกส่วนประกอบ (Separation of Concerns) อย่างเคร่งครัด แนวคิด Clean Architecture คือการป้องกันไม่ให้ส่วนประกอบภายใน (เช่น Business Logic) ถูกผูกติด (Coupled) กับรายละเอียดภายนอก (เช่น Database Implementation) บทความนี้จะเจาะลึกถึงการนำหลักการ Gateway Pattern และ Dependency Inversion Principle (DIP) มาประยุกต์ใช้ในบริบทของ PostgreSQL เพื่อสร้างชั้นการเข้าถึงข้อมูลที่แข็งแกร่งและทดสอบได้

Understanding the Database Gateway Pattern

Database Gateway ทำหน้าที่เป็นชั้นนามธรรม (Abstraction Layer) ระหว่าง Business Logic กับการเรียกใช้ SQL โดยมีเป้าหมายคือการทำให้ Business Logic ไม่จำเป็นต้องรู้ว่าข้อมูลมาจากตารางใด หรือใช้ Dialect SQL แบบใด การใช้ Gateway ช่วยให้เราสามารถเปลี่ยนระบบฐานข้อมูลได้ง่ายเพียงแค่เปลี่ยนโค้ดในชั้น Gateway เท่านั้น โดยที่ Business Logic หลักยังคงทำงานได้ตามปกติ

Applying Dependency Inversion Principle (DIP)

ตามหลัก DIP, โมดูลระดับสูง (High-level modules) ไม่ควรขึ้นอยู่กับโมดูลระดับต่ำ (Low-level modules) แต่ควรขึ้นอยู่กับ Abstraction (Interface) แทน ในบริบทของ PostgreSQL เราสามารถจำลอง Interface นี้ได้โดยการสร้าง PostgreSQL Functions หรือ Views ที่ทำหน้าที่เป็นสัญญา (Contract) ในการดึงข้อมูล ทำให้โค้ดที่เรียกใช้ข้อมูล (Application Layer) พึ่งพาแค่ชื่อ Function/View ไม่ใช่โครงสร้างตารางโดยตรง

PostgreSQL Implementation Example: The Gateway

สมมติว่าเรามีตาราง `user_data` และเราต้องการให้ Business Logic ดึงข้อมูลผู้ใช้โดยไม่สนใจว่าข้อมูลจะถูกจัดเก็บอย่างไร เราจะสร้าง Function ที่ทำหน้าที่เป็น Gateway:

SQL

-- 1. The Underlying Data Table (Low Level) --
CREATE TABLE user_data (
    user_id SERIAL PRIMARY KEY,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    email TEXT
);

-- 2. The Gateway Function (Abstraction Layer) --
-- Function นี้ทำหน้าที่เป็น 'สัญญา' ในการดึงข้อมูล
CREATE OR REPLACE FUNCTION get_user_profile(p_user_id INT) RETURNS TABLE (id INT, full_name TEXT, contact_email TEXT) AS $$ 
BEGIN
    RETURN QUERY SELECT 
        ud.user_id, 
        ud.first_name || ' ' || ud.last_name, 
        ud.email
    FROM user_data ud
    WHERE ud.user_id = p_user_id;
END; 
$$ LANGUAGE plpgsql;

-- 3. Usage (Application Layer - Depends only on the function signature) --
SELECT * FROM get_user_profile(1);

ในตัวอย่างนี้, Business Logic จะเรียกใช้ `get_user_profile(1)` ซึ่งเป็น Gateway Function โดยตรง มันไม่จำเป็นต้องรู้ว่าข้อมูลถูกเก็บในตาราง `user_data` และไม่จำเป็นต้องรู้ว่าต้องมีการต่อสตริง (Concatenation) ชื่อและนามสกุลด้วยตัวเอง การเปลี่ยนแปลงโครงสร้างตาราง (เช่น เปลี่ยนชื่อคอลัมน์) จะส่งผลกระทบเฉพาะที่ Function นี้เท่านั้น

Conclusion

การใช้ Database Gateway Pattern ร่วมกับหลักการ Dependency Inversion ใน PostgreSQL ไม่เพียงแต่ช่วยให้โค้ดของเราสะอาดขึ้นเท่านั้น แต่ยังเพิ่มความยืดหยุ่น (Flexibility) และความสามารถในการทดสอบ (Testability) อย่างมาก ด้วยการแยกชั้นการเข้าถึงข้อมูลออกจาก Business Logic อย่างชัดเจน ทำให้ระบบของเราสามารถรองรับการขยายตัวและการเปลี่ยนแปลงทางเทคนิคในอนาคตได้อย่างราบรื่นและมีประสิทธิภาพสูงสุด

Exit mobile version