ในฐานะ Senior Architect การออกแบบระบบที่ยั่งยืนคือหัวใจสำคัญ และหนึ่งในปัญหาที่นักพัฒนามักเผชิญคือ Leaky Abstractions (การรั่วไหลของนามธรรม) ซึ่งหมายถึงการที่รายละเอียดการทำงานของชั้นล่าง (เช่น ฐานข้อมูล) เข้ามาปะปนและส่งผลกระทบต่อชั้นบน (เช่น Business Logic) ทำให้ระบบมีความเปราะบางและยากต่อการบำรุงรักษา
Clean Architecture เสนอแนวทางในการแยกความกังวล (Separation of Concerns) โดยให้ Domain Rules เป็นศูนย์กลาง ซึ่งเมื่อใช้กับระบบที่ใช้ PostgreSQL เราต้องระมัดระวังไม่ให้ Business Logic ถูกผูกติดกับ SQL Dialect หรือโครงสร้างตารางโดยตรง บทความนี้จะแสดงให้เห็นว่าเราจะใช้ PostgreSQL อย่างไรเพื่อสร้างขอบเขตที่ชัดเจนและลดการรั่วไหลของนามธรรมเหล่านั้น
💡 ปัญหา: Leaky Abstractions ใน PostgreSQL
การรั่วไหลมักเกิดขึ้นเมื่อเราเขียน Business Logic ในเลเยอร์ Application โดยตรง และต้องเรียกใช้ SQL ที่ซับซ้อนเพื่อจัดการกับข้อมูล เช่น การคำนวณยอดรวมที่ต้องใช้ `JOIN` และ `GROUP BY` ที่ซับซ้อนเกินความจำเป็น การที่โค้ดส่วนบนต้องรู้รายละเอียดของ `JOIN` หรือ `WHERE` clause ของตาราง ทำให้เกิดการพึ่งพาฐานข้อมูลมากเกินไป
🛡️ แนวทางแก้ไข: การใช้ PostgreSQL Functions เพื่อห่อหุ้ม Domain Logic
วิธีที่ดีที่สุดในการป้องกัน Leaky Abstractions คือการยกระดับ Business Rules ให้เข้าไปอยู่ในชั้นที่ใกล้กับข้อมูลที่สุด นั่นคือการใช้ Stored Procedures หรือ Functions ของ PostgreSQL เพื่อทำหน้าที่เป็น ‘Gatekeeper’ ของ Domain Logic แทนที่จะให้ Application Layer เขียน SQL เองทั้งหมด
แทนที่จะให้ Application Layer เขียนโค้ดเช่น: SELECT * FROM orders WHERE user_id = $1 AND status = 'COMPLETED' เราควรสร้างฟังก์ชันที่รับเพียง Input ที่เป็นนามธรรม (เช่น User ID และสถานะที่ต้องการ) และให้ฟังก์ชันนั้นจัดการความซับซ้อนของ SQL ทั้งหมด
SQL
-- สร้างตารางจำลอง (Entity) เพื่อให้เห็นภาพการรั่วไหล<pre>CREATE TABLE orders (order_id SERIAL PRIMARY KEY, user_id INT, status VARCHAR(50), total_amount NUMERIC);
SQL
CREATE OR REPLACE FUNCTION get_completed_orders_for_user(p_user_id INT) RETURNS TABLE (order_id INT, total_amount NUMERIC) AS $$
การใช้งาน: Application Layer จะเรียกใช้แค่ SELECT * FROM get_completed_orders_for_user(101); โดยไม่จำเป็นต้องรู้ว่าภายในมี JOIN หรือ WHERE clause ใดซับซ้อน
✅ สรุปการป้องกันการรั่วไหล
เมื่อ Business Logic ถูกย้ายเข้าไปอยู่ใน PostgreSQL Function แล้ว Application Layer จะเหลือเพียงหน้าที่ในการเรียกใช้ (Invoker) เท่านั้น ไม่ต้องสนใจว่าข้อมูลจะถูกดึงมาอย่างไร (How) แต่สนใจแค่ว่าต้องได้ผลลัพธ์อะไร (What) การทำเช่นนี้ช่วยให้เราสามารถเปลี่ยนแปลงโครงสร้างตาราง (Schema) หรือปรับปรุงความซับซ้อนของ Query ภายใน Function ได้ โดยที่ Application Layer ที่เรียกใช้ฟังก์ชันนี้จะไม่ได้รับผลกระทบ ทำให้ระบบมีความยืดหยุ่น (Decoupling) และเป็นไปตามหลัก Clean Architecture อย่างแท้จริง