PlusMagi's Blog By Pitt Phunsanit Backend Dependency Injection, Factory Pattern ,Repository Pattern

Dependency Injection, Factory Pattern ,Repository Pattern

ในฐานะนักพัฒนาซอฟต์แวร์ระดับ Senior Architect การเข้าใจ Design Patterns ไม่ใช่แค่การเพิ่มความรู้ แต่คือการเพิ่มความสามารถในการออกแบบระบบที่ยั่งยืน (Sustainable System) บทความนี้จะเจาะลึกถึงสามรูปแบบที่ถือเป็นรากฐานสำคัญในการเขียนโค้ดระดับมืออาชีพ ได้แก่ Dependency Injection (DI), Factory Pattern และ Repository Pattern

การใช้รูปแบบเหล่านี้จะช่วยให้โค้ดของคุณมีความสะอาด (Clean Code), สามารถดูแลรักษาได้ง่าย (Maintainable), และที่สำคัญที่สุดคือสามารถทดสอบได้ง่าย (Testable) โดยเฉพาะอย่างยิ่งเมื่อต้องทำงานร่วมกับฐานข้อมูลที่ซับซ้อนอย่าง PostgreSQL


💡 Dependency Injection (DI): การส่งมอบสิ่งที่จำเป็น

แนวคิดหลัก: DI คือหลักการของการควบคุมแบบกลับด้าน (Inversion of Control – IoC) แทนที่ Class หนึ่งจะสร้าง Object ที่ตัวเองต้องใช้ (Hard-coded dependency) เราจะรับ Object นั้นจากภายนอกเข้ามาแทน เปรียบเสมือนการส่งเครื่องมือที่พร้อมใช้งานให้ช่าง แทนที่ให้ช่างไปสร้างเครื่องมือเอง

ปัญหาที่แก้ไข: หาก Service Layer ของคุณสร้าง Connection Object เอง เมื่อมีการเปลี่ยน Connection Pool หรือ Driver คุณจะต้องแก้ไขโค้ด Service Layer นั้นทั้งหมด

การประยุกต์ใช้กับ PostgreSQL: แทนที่จะให้ Service Layer เชื่อมต่อ PostgreSQL โดยตรง เราควร Inject ‘Connection Service’ เข้าไปแทน ซึ่งเป็น Interface ที่กำหนดว่าต้องมีเมธอด `execute_query()` เท่านั้น ทำให้เราสามารถสลับไปใช้ Mock Connection สำหรับการทดสอบ หรือเปลี่ยนไปใช้ Connection Pool อื่นได้โดยไม่กระทบ Service Layer

SQL

-- สมมติว่าเรามี Interface ConnectionService<br>-- และ Service Layer ไม่รู้ว่ามันมาจากไหน<br><br>-- ในโค้ดจริง: DatabaseService(ConnectionService connectionService) { ... }<br><br>-- ตัวอย่างการใช้ Connection Service ที่ถูก Inject เข้ามา:<br>SELECT * FROM users WHERE id = 1;<br>-- โค้ดนี้จะทำงานได้ ไม่ว่า connectionService จะเป็น Postgres หรือ Mock DB

🏭 Factory Pattern: โรงงานสร้าง Object

แนวคิดหลัก: Factory Pattern คือการสร้าง Object ผ่านตัวกลาง (Factory) โดยที่ Client Code ไม่จำเป็นต้องรู้ว่ากำลังสร้าง Object ประเภทใดอย่างเจาะจง เพียงแค่ระบุ ‘เงื่อนไข’ (เช่น ประเภทของฐานข้อมูล) และ Factory จะรับผิดชอบในการสร้าง Object ที่ถูกต้องและเหมาะสมกลับมาให้

การประยุกต์ใช้กับ PostgreSQL: เมื่อระบบของคุณต้องรองรับการเชื่อมต่อฐานข้อมูลหลายประเภท (เช่น PostgreSQL, MySQL, SQLite) Factory จะทำหน้าที่ตัดสินใจว่าจะสร้าง Object Connection Type ใดให้เราตาม Input ที่ได้รับ

SQL

-- ตัวอย่างการใช้ Factory เพื่อเลือก Connection Handler<br>SELECT create_connection_handler('postgres', 'user', 'pass');<br><br>-- Internal logic (จำลอง):<br>CREATE FUNCTION create_connection_handler(db_type TEXT, user TEXT, pass TEXT) RETURNS connection_handler AS $$<br>BEGIN<br>    IF db_type = 'postgres' THEN<br>        RETURN 'PostgresHandler';<br>    ELSIF db_type = 'mysql' THEN<br>        RETURN 'MySQLHandler';<br>    ELSE<br>        RAISE EXCEPTION 'Unsupported DB Type';<br>    END IF;<br>END;$$ LANGUAGE plpgsql;

💾 Repository Pattern: การแยกชั้นข้อมูล

แนวคิดหลัก: Repository Pattern คือการสร้างชั้นนามธรรม (Abstraction Layer) ระหว่าง Business Logic กับ Data Access Logic โดยมันจะทำหน้าที่เสมือนเป็นคลังเก็บข้อมูล (In-memory collection) ทำให้ Service Layer สามารถเรียกใช้ข้อมูลได้เหมือนกำลังเรียกใช้ Collection ในหน่วยความจำ โดยไม่จำเป็นต้องรู้ว่าข้อมูลนั้นถูกเก็บใน PostgreSQL, NoSQL หรือไฟล์ใด

การประยุกต์ใช้กับ PostgreSQL: เมื่อคุณต้องการดึงข้อมูลผู้ใช้ (User) Repository จะรับผิดชอบการเขียน SQL ทั้งหมด ไม่ว่าจะเป็น `SELECT * FROM users WHERE id = $1` หรือการจัดการ Transaction ต่างๆ ทำให้ Business Logic สะอาดและไม่ปะปนกับไวยากรณ์ SQL

SQL

-- ตัวอย่าง Repository Method (PostgreSQL Function)<br>-- Repository จะซ่อนรายละเอียดการ Query ทั้งหมดไว้<br>CREATE FUNCTION get_user_by_id(user_id INT) RETURNS TABLE (id INT, name TEXT, email TEXT) AS $$<br>BEGIN<br>    RETURN QUERY SELECT id, name, email FROM users WHERE id = $1;<br>END;$$ LANGUAGE plpgsql;<br><br>-- Service Layer เรียกใช้แค่: repository.get_user_by_id(1);

✅ บทสรุป: การทำงานร่วมกันของ Design Patterns

ทั้งสามรูปแบบนี้ไม่ได้ทำงานแยกกัน แต่ทำงานร่วมกันเพื่อสร้างสถาปัตยกรรมที่แข็งแกร่ง:

  1. Factory ใช้ในการสร้าง Object ที่เหมาะสม (เช่น Connection Object)
  2. Dependency Injection ใช้ในการส่งมอบ Object ที่สร้างโดย Factory นั้น เข้าไปให้ Service Layer
  3. Repository ใช้เป็นตัวกลางในการเข้าถึงข้อมูล โดยใช้ Connection Object ที่ถูก Inject เข้ามา

การเข้าใจและประยุกต์ใช้รูปแบบเหล่านี้อย่างถูกต้อง จะช่วยยกระดับโค้ดของคุณให้เป็นระดับ Enterprise-Grade ที่พร้อมต่อการขยายตัวและการบำรุงรักษาในระยะยาว

Exit mobile version