ในฐานะ Senior Architect การออกแบบระบบที่มีความยืดหยุ่น (Flexibility) และสามารถบำรุงรักษาได้ง่าย (Maintainability) คือหัวใจสำคัญของงานวิศวกรรมซอฟต์แวร์และฐานข้อมูล Dependency Injection (DI) คือรูปแบบการออกแบบ (Design Pattern) ที่ไม่ได้จำกัดอยู่แค่ภาษาโปรแกรมมิ่งเท่านั้น แต่เป็นหลักการทางสถาปัตยกรรมที่ช่วยให้เราลดการยึดติด (Coupling) ระหว่างส่วนประกอบต่าง ๆ ของระบบ
ในบริบทของ PostgreSQL ซึ่งเป็นระบบฐานข้อมูลเชิงสัมพันธ์ (Relational Database) การนำหลักการ DI มาใช้ไม่ได้หมายถึงการ ‘ฉีด’ Object ผ่าน Constructor โดยตรง แต่หมายถึงการออกแบบให้โมดูล (Modules) หรือฟังก์ชัน (Functions) ต่าง ๆ สามารถทำงานร่วมกันได้โดยอาศัยการส่งผ่าน ‘การพึ่งพา’ (Dependencies) ผ่านพารามิเตอร์อย่างชัดเจน ทำให้ระบบโดยรวมมีความเป็นอิสระและทดสอบได้ง่ายขึ้น
หลักการทำงาน: การแยกส่วนประกอบ (Decoupling) ใน PostgreSQL
ตามหลักการทั่วไป หากเรามีฟังก์ชันหลัก (Main Function) ที่ต้องทำงานหลายอย่าง เช่น การประมวลผลคำสั่งซื้อ (Order Processing) หากเราเขียนโค้ดแบบเดิม (Tight Coupling) ฟังก์ชันนี้จะเรียกใช้การตรวจสอบสินค้าคงคลัง (Inventory Check) และการคำนวณภาษี (Tax Calculation) โดยการเรียกใช้โค้ดเหล่านั้นโดยตรง ทำให้หากเราต้องการเปลี่ยนวิธีการคำนวณภาษี เราต้องเข้าไปแก้ไขโค้ดในฟังก์ชันหลักด้วย
แต่ด้วยหลักการ DI เราจะออกแบบให้ฟังก์ชันหลักเพียงแค่ ‘ร้องขอ’ (Request) บริการเหล่านั้นผ่านพารามิเตอร์ (Parameters) หรือการเรียกใช้ฟังก์ชันย่อยที่แยกส่วนออกมาอย่างชัดเจน ทำให้ฟังก์ชันหลักไม่จำเป็นต้องรู้ว่าเบื้องหลังของบริการนั้น ๆ ถูกเขียนด้วยวิธีการใด
SQL
-- ตัวอย่างการออกแบบที่ใช้หลักการ DI ในเชิงสถาปัตยกรรมฐานข้อมูล (Modular Design) --
-- 1. สร้าง Dependency (บริการที่ถูกพึ่งพา): การคำนวณภาษี
CREATE OR REPLACE FUNCTION calculate_tax(amount NUMERIC) RETURNS NUMERIC AS $$
BEGIN
RETURN amount * 0.07; -- Dependency 1
END;
$$ LANGUAGE plpgsql;
-- 2. สร้าง Dependency (บริการที่ถูกพึ่งพา): การตรวจสอบสินค้าคงคลัง
CREATE OR REPLACE FUNCTION check_inventory(product_id TEXT, quantity INT) RETURNS BOOLEAN AS $$
SELECT EXISTS (SELECT 1 FROM inventory WHERE product_id = $1 AND stock >= $2); -- Dependency 2
$$ LANGUAGE sql;
-- 3. Main Service (ฟังก์ชันหลัก): รับ Dependencies เข้ามาทำงาน
CREATE OR REPLACE FUNCTION process_order(order_id INT, product_id TEXT, quantity INT, tax_func FUNCTION, inventory_func FUNCTION) RETURNS BOOLEAN AS $$
DECLARE
total_amount NUMERIC := 100.00;
tax_amount NUMERIC;
BEGIN
-- Call dependencies via parameters (DI Principle)
IF NOT inventory_func(product_id, quantity) THEN
RAISE EXCEPTION 'Product out of stock.';
END IF;
tax_amount := tax_func(total_amount);
-- Logic continues...
RETURN TRUE;
END;
$$ LANGUAGE plpgsql;
-- การใช้งาน: เราส่งฟังก์ชันที่สร้างไว้เป็น Dependency เข้าไปในฟังก์ชันหลัก
SELECT process_order(101, 'apple', 2, calculate_tax, check_inventory);
ประโยชน์ของการใช้ DI ในงาน Database Architecture
- Loose Coupling (ลดการยึดติด): ฟังก์ชัน `process_order` ไม่ได้รู้ว่า `calculate_tax` ทำงานอย่างไร มันแค่รู้ว่ามันต้องเรียกใช้ฟังก์ชันที่รับค่าตัวเลขและคืนค่าตัวเลขเท่านั้น ทำให้เราสามารถเปลี่ยนวิธีการคำนวณภาษี (เช่น เปลี่ยนจาก 7% เป็น 10%) โดยไม่ต้องแตะโค้ดใน `process_order` เลย
- Testability (ความสามารถในการทดสอบ): ในการทดสอบ เราสามารถ ‘จำลอง’ (Mock) ฟังก์ชัน `check_inventory` ให้คืนค่า `TRUE` เสมอ แม้ว่าในความเป็นจริงสินค้าจะหมด เพื่อทดสอบ Logic ของฟังก์ชันหลักได้อย่างเป็นอิสระ
- Reusability (การนำกลับมาใช้ใหม่): ฟังก์ชันย่อย เช่น `calculate_tax` สามารถถูกเรียกใช้โดยโมดูลอื่น ๆ ได้อย่างอิสระ ไม่ว่าจะเป็นการคำนวณภาษีในระบบสั่งซื้อ หรือระบบออกใบแจ้งหนี้
บทสรุป
แม้ว่า Dependency Injection จะเป็นแนวคิดด้านการออกแบบโค้ด แต่การนำหลักการนี้มาประยุกต์ใช้ในการออกแบบสถาปัตยกรรมของ PostgreSQL ด้วยการใช้ฟังก์ชันย่อย (Modular Functions) และการส่งผ่าน Dependencies อย่างชัดเจน จะช่วยให้ฐานข้อมูลของเรามีความยืดหยุ่นสูงมาก (Highly Decoupled) ทำให้การอัปเกรด การแก้ไข หรือการเพิ่มฟีเจอร์ใหม่ ๆ ในอนาคตทำได้อย่างรวดเร็วและปลอดภัยยิ่งขึ้นในระดับ Senior Architect