ศูนย์รวมความรู้วิศวกรรมซอฟต์แวร์

แบ่งปันประสบการณ์การออกแบบสถาปัตยกรรมระบบ, การเขียนโค้ดด้วย Laravel และการจัดการ Server ระดับ Production เพื่อยกระดับทักษะของนักพัฒนาทุกคน

จาก "แค่ทำงานได้" สู่ "โครงสร้างที่ดี": บอกลา Array Hell สู่โลกที่ปลอดภัยด้วย DTO

การใช้ dto แทนการใ้ช้ array


 การเปลี่ยนจาก Associative Array มาเป็น DTO (Data Transfer Object) คือก้าวสำคัญของการยกระดับซอร์สโค้ดจากการเขียนเพื่อให้ "โปรแกรมรันผ่าน" ไปสู่การสร้าง ระบบที่ยั่งยืนและดูแลรักษาง่าย (Maintainable Architecture)

1. ทำไมเราถึงควรเลิกใช้ Array ส่งข้อมูล (The "Array Hell")
ในการพัฒนา Web Application ขนาดใหญ่ เรามักต้องส่งข้อมูลข้าม Layer (เช่น จาก Controller ไปยัง Service หรือ Action) ซึ่งการใช้ Array ส่งต่อข้อมูลมักสร้างปัญหาชวนปวดหัว ดังนี้:
  • ความไม่แน่นอน (Lack of Structure): เราไม่มีทางรู้เลยว่าใน Array นั้นมี Key อะไรบ้าง จนกว่าจะเข้าไปไล่ดู Code ต้นทาง
  • ไม่มี Type Safety: ข้อมูลใน Array เป็นอะไรก็ได้ (String, Int, หรือ Null) เสี่ยงต่อการเกิด Runtime Error
  • IDE เดาทางไม่ถูก: Tool อย่าง VS Code หรือ Cursor จะไม่ช่วยทำ Autocomplete ให้เลย เพราะไม่รู้โครงสร้างภายใน
  • ยากต่อการ Refactor: หากต้องการเปลี่ยนชื่อ Key เพียงคำเดียว อาจต้องตามแก้กันทั้งโปรเจกต์เพราะค้นหา Reference (ตัวเชื่อมโยง) ได้ยาก
2. DTO คืออะไร?
DTO (Data Transfer Object) คือ Object เรียบง่ายที่ถูกสร้างขึ้นมาเพื่อ "อุ้มข้อมูล" ข้ามกระบวนการ โดยหน้าที่เดียวของมันคือการรวมกลุ่มข้อมูลและกำหนดโครงสร้างที่ชัดเจน (Strict Contract) เพื่อให้ทุก Layer คุยภาษาเดียวกัน

ตารางเปรียบเทียบ: Associative Array vs DTO
คุณสมบัติ
                 | Associative Array             | Data Transfer Object (DTO)
โครงสร้างข้อมูล           |
ยืดหยุ่นเกินไป (Schema-less)  | ชัดเจนและแน่นอน (Strict Schema)
การตรวจสอบชนิดข้อมูล | ทำไม่ได้ทันที (ไม่มี Type)         | มี Type Hinting ชัดเจน
IDE Support               | ไม่มี (ต้องจำ Key เองทั้งหมด) | มี Autocomplete เต็มรูปแบบ
โอกาสเกิดความผิดพลาด | พิมพ์ Key ผิดได้ง่าย (Typo)   | ตรวจจับเจอตั้งแต่ตอนเขียน (Static Analysis)

3. เปรียบเทียบด้วย Code (PHP 8+)
ลองดูความแตกต่างเมื่อเราต้องการจัดการข้อมูลระบบสมัครสมาชิก (User Registration)

  • แบบเดิม: การใช้ Array (เสี่ยงและสับสน)
---------------------------------------------------------------------------------------------------
public function store(array $data)
{
    // เราไม่รู้เลยว่าต้องเรียก $data['user_name'] หรือ $data['username'] กันแน่?
    // และถ้าค่าเป็น null โดยที่เราไม่รู้ ระบบก็อาจจะพังทันที
    return User::create([
        'name'  => $data['name'],
        'email' => $data['email'],
    ]);
}
---------------------------------------------------------------------------------------------------
  •  แบบใหม่: การใช้ DTO (ปลอดภัยและเป็นมืออาชีพ) 
---------------------------------------------------------------------------------------------------
readonly class UserRegistrationDTO
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $phoneNumber = null, // บอกชัดเจนว่าเป็น Optional (ใส่หรือไม่ใส่ก็ได้)
    ) {}

    // Factory Method สำหรับแปลง Request เป็น DTO ตัวเอง
    public static function fromRequest(Request $request): self
    {
        return new self(
            name: $request->validated('name'),
            email: $request->validated('email'),
            phoneNumber: $request->validated('phone'),
        );
    }
}

// 🚀 การใช้งานใน Service / Action Layer
public function execute(UserRegistrationDTO $data)
{
    // IDE จะรู้ทันทีว่า $data มี property อะไรให้เรียกใช้บ้างผ่าน Autocomplete
    return User::create([
        'name'  => $data->name,
        'email' => $data->email,
    ]);
}
---------------------------------------------------------------------------------------------------

4. ข้อดีของการใช้ DTO ในโปรเจกต์จริง
1) Self-Documenting Code
DTO ทำหน้าที่เป็นเอกสารอธิบายตัวเองในโค้ด เมื่อนักพัฒนาคนอื่นในทีมมาอ่านซอร์สโค้ดต่อ จะรู้ได้ทันทีว่าข้อมูลที่ไหลอยู่ในระบบมีหน้าตาและเงื่อนไขอย่างไร โดยไม่ต้องเดา
2) มั่นใจด้วย Type Safety & Immutability
การใช้ DTO ร่วมกับฟีเจอร์ readonly ใน PHP 8 ช่วยรับประกันว่าข้อมูลจะไม่ถูกแอบแก้ไขระหว่างทาง (Immutability) และได้ชนิดข้อมูลที่ถูกต้องแม่นยำเสมอ
3) ตัวต่อสำคัญใน Action-DTO-Query Pattern
สำหรับระบบที่ออกแบบโครงสร้างสถาปัตยกรรม (Architecture) แขนงนี้ DTO จะทำหน้าที่เป็นสะพานเชื่อมที่ไร้รอยต่อ:
  • Controller: รับ Request เข้ามา ➡️ ตรวจสอบความถูกต้อง ➡️ แปลงเป็น DTO
  • Action/Service: รับ DTO ไปประมวลผล Business Logic ต่ออย่างปลอดภัย
  • Query: รับ DTO ไปคัดกรองข้อมูลจาก Database
สรุปเพื่อการทบทวน (Key Takeaways)
  • Array เหมาะกับข้อมูลชั่วคราวที่ใช้จบภายใน Function สั้นๆ เท่านั้น
  • DTO เหมาะกับการส่งข้อมูลข้าม Class ข้าม Layer หรือใช้เป็นมาตรฐานการสื่อสารในระบบ
  • การใช้ DTO ช่วยลดเวลาการ Debug เพราะจะพบ Error ตั้งแต่ขั้นตอนการเขียนโค้ด (เมื่อใช้ร่วมกับ Tool อย่าง PHPStan หรือ Larastan)
  • แม้จะรู้สึกว่าต้องเขียนโค้ดเพิ่มขึ้นในตอนแรก (Boilerplate) แต่ในระยะยาว DTO จะช่วยลดหนี้ทางเทคนิค (Technical Debt) ของระบบได้อย่างมหาศาล


laravel