5 คะแนน โดย GN⁺ 2025-03-29 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • แอปพลิเคชัน Python ที่ซับซ้อน เมื่อมีขนาดใหญ่ขึ้น การทดสอบและการเปลี่ยนแปลงด้วยวิธีการพัฒนาแบบเรียบง่ายอย่างเดียวจะรับมือได้ยาก หนังสือเล่มนี้จึงนำเสนอ แพตเทิร์นสถาปัตยกรรม เป็นทางออกผ่านตัวอย่างโค้ด
  • ใช้การขายเฟอร์นิเจอร์ออนไลน์ของ MADE.com และ ซัพพลายเชนระดับโลก เป็นตัวอย่าง โดยให้ความสำคัญกับการทำโดเมนโมเดลลิงที่ย้ายปัญหาการดำเนินงานจริงมาเป็นโมเดลซอฟต์แวร์
  • แกนหลักคือ TDD, DDD และสถาปัตยกรรมแบบ event-based โดยมีเป้าหมายคือ unit test ที่รวดเร็ว, E2E test จำนวนน้อย, โมเดลที่แยกจากโครงสร้างพื้นฐาน และการเชื่อมรวมกันด้วยข้อความ
  • ใช้ Flask, SQLAlchemy, pytest, Docker, Redis แต่เน้นการสร้างโครงสร้างที่ทำให้การเลือกเทคโนโลยีเฉพาะถูกผลักไปเป็น รายละเอียดการพัฒนา
  • หากเคยมีประสบการณ์จัดการแอปพลิเคชัน Python ที่ซับซ้อนก็สามารถอ่านได้ และไม่จำเป็นต้องรู้ DDD หรือแพตเทิร์นสถาปัตยกรรมแอปพลิเคชันแบบคลาสสิกมาก่อน

จุดเริ่มต้นจากโจทย์เรื่องโครงสร้างที่ทดสอบง่าย

  • หลังจากหนังสือเล่มก่อนของ Harry คือ Test-Driven Development with Python ยังมีคำถามค้างอยู่ว่าควรจัดโครงสร้างแอปพลิเคชันอย่างไรให้ทดสอบง่าย
  • ประเด็นสำคัญคือการสร้างโครงสร้างที่ครอบคลุม ตรรกะทางธุรกิจ ด้วย unit test ได้เพียงพอ พร้อมลดจำนวน integration test และ E2E test ให้น้อยที่สุด
  • แม้จะกล่าวถึงแนวคิดอย่าง "Hexagonal Architecture", "Ports and Adapters", "Functional Core, Imperative Shell" แต่ในตอนนั้นยังไม่ได้เข้าใจหรือปฏิบัติได้อย่างเพียงพอจริง ๆ
  • Bob กลายมาเป็นสถาปนิกเพราะในทีมไม่มีคนรับผิดชอบสถาปัตยกรรมซอฟต์แวร์ และได้เรียนรู้แนวทางใหม่ในการเขียนโค้ดและวิธีคิดจาก Ian Cooper

กรณี MADE.com: โมเดลซัพพลายเชนเป็นซอฟต์แวร์

  • ผู้เขียนทั้งสองทำงานที่บริษัทอีคอมเมิร์ซยุโรป MADE.com และนำเทคนิคในหนังสือไปใช้สร้าง ระบบกระจายศูนย์ที่โมเดลปัญหาธุรกิจจริง
  • โดเมนตัวอย่างอิงจากระบบแรกที่ Bob สร้างที่ MADE โดยมีเป้าหมายเพื่อจัดระเบียบสิ่งที่ต้องสอนเมื่อโปรแกรมเมอร์ใหม่เข้าร่วมทีม
  • MADE.com ดำเนินงาน ซัพพลายเชนระดับโลก ที่ประกอบด้วยพาร์ตเนอร์ขนส่งสินค้าและผู้ผลิต
    • พยายามปรับการจัดส่งให้เหมาะสมเพื่อไม่ให้สต็อกค้างอยู่ในคลังนานเกินไป เพื่อลดต้นทุน
    • ในอุดมคติ ต้องการให้โซฟามาถึงท่าเรือในวันเดียวกับที่ลูกค้าซื้อ แล้วจัดส่งถึงบ้านลูกค้าโดยไม่ต้องเก็บเข้าคลัง
    • สินค้าอาจใช้เวลา 3 เดือนกว่าจะมาถึงโดยเรือคอนเทนเนอร์ ทำให้การปรับจังหวะเวลาเป็นเรื่องยาก
  • ในการปฏิบัติงานจริง ตัวแปรอย่างความเสียหาย น้ำท่วม ความล่าช้าจากพายุ ความผิดพลาดในการจัดการของพาร์ตเนอร์โลจิสติกส์ เอกสารตกหล่น และการเปลี่ยนแปลงคำสั่งซื้อของลูกค้าเกิดขึ้นอย่างต่อเนื่อง
  • เพื่อจัดการปัญหาเหล่านี้ จึงพยายามแสดงแทนการดำเนินงานในโลกจริงด้วยซอฟต์แวร์ และ ทำให้งานเป็นอัตโนมัติ ให้มากที่สุดเท่าที่ทำได้

ข้อจำกัดที่ปรากฏเมื่อโปรเจกต์ Python เติบโต

  • Python เติบโตและเติบโตเต็มที่ขึ้นอย่างรวดเร็ว แต่ถูกมองว่าเพิ่งเริ่มรับบทบาทในการจัดการปัญหาประเภทที่ฝั่ง C# และ Java รับมือกันมานานอย่างจริงจัง
  • สตาร์ทอัพกลายเป็นธุรกิจจริง เว็บแอปและสคริปต์อัตโนมัติก็เติบโตเป็น ซอฟต์แวร์ระดับองค์กร
  • ในปรัชญาของ Python มีคำว่า “There should be one—and preferably only one—obvious way to do it” แต่เมื่อโปรเจกต์มีขนาดใหญ่ขึ้น วิธีที่ obvious ที่สุดไม่ได้เหมาะกับการจัดการความซับซ้อนและการเปลี่ยนแปลงของความต้องการเสมอไป
  • เทคนิคและแพตเทิร์นที่หนังสือกล่าวถึงไม่ใช่ของใหม่ แต่ส่วนใหญ่ถือว่าค่อนข้างใหม่ในโลก Python
  • หนังสือไม่ได้ตั้งใจแทนที่ Domain-Driven Design ของ Eric Evans หรือ Patterns of Enterprise Application Architecture ของ Martin Fowler และมักอ้างถึงพร้อมแนะนำให้อ่านคลาสสิกเหล่านั้น
  • ตัวอย่างโค้ดในเอกสารเดิมมักเขียนด้วย Java หรือ C++/# จึงอาจเป็นภาระในการอ่านสำหรับนักพัฒนา Python

เครื่องมือสามอย่างสำหรับจัดการความซับซ้อน

  • การพัฒนาแบบขับเคลื่อนด้วยการทดสอบ (TDD) ช่วยให้สร้างโค้ดที่ถูกต้อง และรีแฟกเตอร์หรือเพิ่มฟีเจอร์ได้โดยไม่กลัว regression
    • วิธีรันทดสอบให้เร็วที่สุดเท่าที่เป็นไปได้
    • วิธีได้ coverage และ feedback ให้มากที่สุดจาก unit test ที่เร็วและไม่มี dependency
    • วิธีลด E2E test ที่ช้าและไม่เสถียรให้น้อยที่สุด
  • การออกแบบแบบขับเคลื่อนด้วยโดเมน (DDD) ช่วยให้โฟกัสกับการสร้างโมเดลที่ดีของโดเมนธุรกิจ
    • วิธีไม่ให้โมเดลถูกผูกติดกับข้อกังวลด้านโครงสร้างพื้นฐาน
    • วิธีไม่ให้โมเดลกลายเป็นสิ่งที่เปลี่ยนแปลงได้ยาก
  • ไมโครเซอร์วิสที่เชื่อมรวมกันด้วยข้อความและ coupling ต่ำ ซึ่งบางครั้งเรียกว่า reactive microservices ถูกกล่าวถึงในฐานะทางออกสำหรับจัดการความซับซ้อนระหว่างหลายแอปพลิเคชันหรือหลายโดเมนธุรกิจ
    • การจะทำให้เข้ากับเครื่องมือเดิมในระบบนิเวศ Python เช่น Flask, Django, Celery อย่างไรนั้นไม่ได้ชัดเจนเสมอไป
  • แม้จะไม่ใช้หรือไม่สนใจไมโครเซอร์วิส แพตเทิร์นส่วนใหญ่ในหนังสือและเนื้อหาจำนวนมากเกี่ยวกับสถาปัตยกรรมแบบ event-based ก็ยังประยุกต์ใช้กับ สถาปัตยกรรมแบบโมโนลิท ได้

ผู้อ่านและความรู้พื้นฐาน

  • หนังสือเล่มนี้สมมติว่าผู้อ่านเคยทำงานใกล้ชิดกับแอปพลิเคชัน Python ที่ซับซ้อนในระดับหนึ่ง
  • เหมาะกับผู้อ่านที่เคยสัมผัสความเจ็บปวดจากการจัดการความซับซ้อน
  • ไม่จำเป็นต้องมีความรู้ล่วงหน้าเกี่ยวกับ DDD หรือแพตเทิร์นสถาปัตยกรรมแอปพลิเคชันแบบคลาสสิก
  • การอธิบายจะค่อย ๆ สะสมไปทีละบทโดยอิงจากแอปพลิเคชันตัวอย่าง
  • ผู้เขียนใช้ TDD ในงาน จึงมักใช้ลำดับที่แสดงโค้ดทดสอบก่อน แล้วตามด้วยการพัฒนา
  • ใช้ Flask, SQLAlchemy, pytest, Docker, Redis แต่หากคุ้นเคยอยู่แล้วก็แค่ช่วยให้อ่านง่ายขึ้น ไม่ใช่ข้อกำหนด
  • หนึ่งในเป้าหมายหลักคือการสร้างสถาปัตยกรรมที่ทำให้การเลือกเทคโนโลยีเฉพาะกลายเป็น รายละเอียดการพัฒนาเล็กน้อย

โครงสร้างของหนังสือ

  • หนังสือแบ่งออกเป็นสองส่วน
  • ส่วนที่ 1: สถาปัตยกรรมที่สนับสนุนโดเมนโมเดลลิง

    • โดเมนโมเดลลิงและ DDD กล่าวถึงในบทที่ 1, 2 และ 7
    • ตั้งอยู่บนสมมติฐานว่าปัญหาธุรกิจที่ซับซ้อนควรถูกสะท้อนเป็นโดเมนโมเดลในโค้ด
    • อธิบายวิธีเริ่มจากโมเดลที่ไม่มี dependency ภายนอกและทำ unit test ได้รวดเร็ว
    • จากนั้นกลับมาพูดถึงการเลือก aggregate ที่เหมาะสมและปัญหาความถูกต้องครบถ้วนของข้อมูล
    • แพตเทิร์น Repository, Service Layer, Unit of Work กล่าวถึงในบทที่ 2, 4 และ 5
    • แพตเทิร์นเหล่านี้เสริมกันเพื่อแยกโมเดลออกจาก dependency ที่ไม่จำเป็น
    • สร้างชั้น abstraction รอบ persistent storage และมี service layer ที่จับ entry point ของระบบและ use case หลัก
    • แสดงโครงสร้างที่ทำให้สร้าง entry point แบบบาง เช่น Flask API หรือ CLI ได้ง่าย
    • การอภิปรายเรื่อง abstraction และการทดสอบต่อเนื่องในบทที่ 3 และ 5
    • หลังแนะนำ Repository pattern แล้ว จะกล่าวถึงวิธีเลือก abstraction และบทบาทของ abstraction ในวิธีการ coupling
    • หลัง Service Layer pattern จะกล่าวถึง test pyramid และวิธีเขียน unit test ในระดับ abstraction ที่สูงที่สุดเท่าที่เป็นไปได้
  • ส่วนที่ 2: สถาปัตยกรรมแบบ event-based

    • สถาปัตยกรรมแบบ event-based กล่าวถึงตั้งแต่บทที่ 8 ถึง 11
    • แนะนำแพตเทิร์น Domain Events, Message Bus, Handler
    • Domain Events เป็นวิธีบรรจุแนวคิดว่าปฏิสัมพันธ์บางอย่างของระบบกระตุ้นให้เกิดการทำงานอื่น
    • Message Bus ทำให้การกระทำกระตุ้น event และเรียก handler ที่เหมาะสม
    • กล่าวถึงวิธีใช้ event เป็นแพตเทิร์นการเชื่อมรวมระหว่างบริการในสถาปัตยกรรมไมโครเซอร์วิส
    • แยกความแตกต่างระหว่าง commands และ events และโดยสาระแล้ว แอปพลิเคชันจะกลายเป็นระบบประมวลผลข้อความ
    • บทที่ 12 กล่าวถึงตัวอย่าง CQRS ทั้งแบบที่ใช้ event และไม่ใช้ event
    • บทที่ 13 จัดระเบียบ dependency แบบชัดแจ้งและแฝง และพัฒนาเฟรมเวิร์ก dependency injection อย่างง่าย
    • บทส่งท้ายเสนอวิธีนำหลักการเหล่านี้ไปใช้กับซอฟต์แวร์เดิม แทนที่จะเป็นตัวอย่างง่าย ๆ ที่เริ่มใหม่ รวมถึงรายการอ่านเพิ่มเติม

โค้ดตัวอย่างและการทำตาม

  • หนังสือจัดโครงสร้างรอบโปรเจกต์ตัวอย่างหนึ่ง และค่อย ๆ ขยายโปรเจกต์ไปทีละขั้นเมื่อบทดำเนินไป
  • ให้ความสำคัญกับการที่ผู้อ่านได้ลองจับโค้ดจริงและสร้างความรู้สึกว่าแพตเทิร์นทำงานอย่างไร
  • โค้ดทั้งหมดอยู่บน GitHub และมี branch แยกสำหรับแต่ละบท
  • แนะนำวิธีทำตามสามแบบ
    • สร้าง repository เองและสร้างแอปตามตัวอย่างในหนังสือ โดยอ้างอิง repository ของผู้เขียนเมื่อจำเป็น
    • ลองนำแพตเทิร์นแต่ละอย่างไปใช้กับโปรเจกต์เล็ก ๆ ของตนเองทีละบท
    • ฝึกอย่างรวดเร็วด้วย "Exercise for the Reader" ของแต่ละบทและโค้ด GitHub ที่เว้นบางส่วนไว้
  • โดยเฉพาะผู้อ่านที่ต้องการนำแพตเทิร์นไปใช้กับโปรเจกต์ของตนเอง การจัดการกับตัวอย่างเรียบง่ายก่อนถือเป็น การฝึกที่ปลอดภัย
  • อย่างน้อยเมื่ออ่านแต่ละบท แนะนำให้รับโค้ดจาก repository ด้วย git checkout และตรวจดูโค้ดในบริบทของแอปที่ทำงานได้จริง

ใบอนุญาตและรูปแบบ

  • โค้ดและเวอร์ชันออนไลน์อยู่ภายใต้ใบอนุญาต Creative Commons CC BY-NC-ND
    • เพื่อวัตถุประสงค์ที่ไม่ใช่เชิงพาณิชย์
    • ต้องแสดงที่มา
    • สามารถคัดลอกและแชร์ได้
  • ฉบับพิมพ์อยู่ภายใต้ใบอนุญาตแยกต่างหาก และหากมีข้อกังวลเกี่ยวกับการนำกลับมาใช้ ให้ติดต่อ O’Reilly
  • ธรรมเนียมการเขียนในหนังสือแยกใช้ตัวเอียง, fixed width, fixed width ตัวหนา และ fixed width ตัวเอียง
    • ตัวเอียงใช้แทนคำศัพท์ใหม่, URL, ที่อยู่อีเมล, ชื่อไฟล์ และนามสกุลไฟล์
    • fixed width ใช้แทนรายการโปรแกรมและองค์ประกอบของโค้ด
    • fixed width ตัวหนาใช้แทนคำสั่งหรือข้อความที่ผู้ใช้ต้องพิมพ์ตามเดิม
    • fixed width ตัวเอียงใช้แทนข้อความที่จะถูกแทนที่ด้วยค่าที่ผู้ใช้ระบุหรือกำหนดตามบริบท

1 ความคิดเห็น

 
xguru 2025-03-29

มีฉบับแปลภาษาเกาหลีออกมาแล้ว Architecture Patterns with Python