สำรวจแพตเทิร์นสถาปัตยกรรมด้วย Python
(cosmicpython.com)- แอปพลิเคชัน 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 แยกสำหรับแต่ละบท
- ดูรายการ branch ได้ที่ หน้า branches ของ GitHub
- แนะนำวิธีทำตามสามแบบ
- สร้าง 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 ความคิดเห็น
มีฉบับแปลภาษาเกาหลีออกมาแล้ว Architecture Patterns with Python