ในโลกของการพัฒนาระบบที่ซับซ้อน การรักษาความยืดหยุ่นและการแยกส่วนประกอบ (Decoupling) คือหัวใจสำคัญของสถาปัตยกรรมซอฟต์แวร์ Clean Architecture คือแนวทางที่ช่วยให้แอปพลิเคชันของเราเป็นอิสระจากรายละเอียดทางเทคนิคภายนอก เช่น ฐานข้อมูลหรือ UI การทำความเข้าใจเรื่อง Use Case Input/Output Ports จึงเป็นสิ่งจำเป็นสำหรับ Senior Architect ที่ต้องการสร้างระบบที่ทนทานและง่ายต่อการบำรุงรักษา
Ports ในบริบทของ PostgreSQL
ใน Clean Architecture, Use Case Ports ทำหน้าที่เป็นสัญญา (Contract) ที่กำหนดว่า Use Case นั้นๆ ต้องการข้อมูลอะไรจากโลกภายนอก (Input Port) และจะส่งผลลัพธ์อะไรกลับไป (Output Port) เมื่อเราใช้ PostgreSQL เป็นระบบหลัก เราสามารถจำลอง Ports เหล่านี้ได้โดยการใช้ฟังก์ชันที่ถูกกำหนดพารามิเตอร์อย่างชัดเจน (Parameterized Functions) และการใช้ประเภทข้อมูลที่ซับซ้อน (Complex Data Types) เช่น `JSONB` หรือ `TEXT[]` เพื่อบังคับใช้รูปแบบข้อมูลที่ Use Case ต้องปฏิบัติตาม
การจำลอง Input Port ด้วย PostgreSQL Functions
Input Port คือจุดที่ Use Case รับข้อมูลเข้า ฟังก์ชันใน PostgreSQL จะทำหน้าที่เป็นตัวรับ Input นี้ โดยเราจะกำหนด Signature ของฟังก์ชันให้ตรงกับข้อมูลที่ Use Case คาดหวัง ตัวอย่างเช่น หาก Use Case ต้องการประมวลผลรายการ ID ผู้ใช้ที่ถูกคั่นด้วยเครื่องหมายคอมมา เราควรใช้ประเภทข้อมูล Array ของ PostgreSQL เพื่อรับประกันความถูกต้องของข้อมูล
SQL
-- 1. สร้างตารางจำลองข้อมูล (Repository Layer)
SQL
CREATE TABLE users (user_id TEXT PRIMARY KEY, username TEXT, status TEXT);
ต่อไปนี้คือการสร้างฟังก์ชันที่ทำหน้าที่เป็น ‘Use Case’ ซึ่งมี Input Port ที่รับ Array ของ User IDs:
SQL
-- 2. สร้างฟังก์ชันที่ทำหน้าที่เป็น Use Case (Input Port Contract)
SQL
CREATE OR REPLACE FUNCTION process_user_batch(user_ids TEXT[])
SQL
RETURNS TABLE (user_id TEXT, processed_status TEXT)
SQL
LANGUAGE plpgsql
SQL
AS $$
SQL
BEGIN
SQL
RETURN QUERY
SQL
SELECT
SQL
user_id,
SQL
'SUCCESS'
SQL
FROM unnest(user_ids) AS un(user_id)
SQL
JOIN users ON users.user_id = un.user_id;
SQL
END;
SQL
$$;
การเรียกใช้ Use Case (Testing the Input Port)
เมื่อเราเรียกใช้ฟังก์ชันนี้ เรากำลังปฏิบัติตามสัญญาของ Input Port โดยการส่ง Array ของ ID ที่ถูกต้อง:
SQL
SELECT * FROM process_user_batch('101'::text, '102'::text, '103'::text');
บทสรุป
การใช้ PostgreSQL Functions ที่มี Signature ชัดเจนช่วยให้เราสามารถจำลอง Use Case Ports ได้อย่างมีประสิทธิภาพ สิ่งนี้ช่วยให้โค้ดของเรามีความเป็นโมดูลสูง (Highly Modular) และที่สำคัญที่สุดคือทำให้เราสามารถทดสอบ Use Case ได้อย่างอิสระ (Testability) โดยไม่จำเป็นต้องพึ่งพาการทำงานของส่วนประกอบอื่น ๆ ทำให้ระบบของเราเป็นไปตามหลักการของ Clean Architecture อย่างแท้จริง