Style Specifics (รายละเอียดเฉพาะของสไตล์)
Domain (หรือ subdomain ในบางกรณี) จะถูกเรียกว่า module ใน architectural style นี้ module สามารถจัดระเบียบได้สองแบบ architecture ที่ง่ายที่สุดคือ monolithic structure ซึ่ง module และ component เชิงตรรกะที่เกี่ยวข้องทั้งหมดจะอยู่ในโค้ดเบสเดียวกัน โดยแบ่งแยกด้วย namespace หรือโครงสร้าง directory ที่มันอยู่ ตัวเลือกที่ซับซ้อนขึ้นมาอีกเล็กน้อยคือ modular structure ซึ่งแต่ละ module จะถูกแทนเป็น artifact ที่เป็นอิสระและจบในตัวเอง (เช่นไฟล์ JAR หรือ DLL) แล้วนำ module เหล่านั้นมารวมกันเป็นซอฟต์แวร์ monolithic หน่วยเดียวในขั้นตอนการ deploy
เช่นเดียวกับทุกสิ่งใน software architecture การเลือกระหว่างตัวเลือกโครงสร้างสองแบบนี้ขึ้นอยู่กับปัจจัยและ trade-off มากมาย หัวข้อต่อไปนี้จะสรุปทั้งสองแบบและพูดถึง trade-off ที่เกี่ยวข้องกัน
Monolithic Structure
ใน monolithic structure module ทั้งหมดที่แทนระบบจะอยู่ใน source-code repository เดียว โค้ดทั้งหมดที่เกี่ยวข้องกับแต่ละ module จะถูก deploy เป็นหน่วยเดียวเมื่อส่งมอบหรือ release ซอฟต์แวร์ ตัวเลือกโครงสร้างนี้แสดงอยู่ใน Figure 11-2 แต่ละ module จะแทนด้วย directory ระดับสูงแยกต่างหาก ซึ่งมี component และ subdomain ใด ๆ ที่ประกอบกันเป็น module นั้น
Figure 11-2. ตัวอย่างของตัวเลือก monolithic structure
namespace ต่อไปนี้แสดงให้เห็นว่า modular monolith อาจมีหน้าตาเป็นอย่างไรสำหรับ architecture ที่แสดงใน Figure 11-2 :
com.orderentry.orderplacement
com.orderentry.inventorymanagement
com.orderentry.paymentprocessing
com.orderentry.notification
com.orderentry.fulfillment
com.orderentry.shipping
นี่คือตัวเลือกที่ง่ายที่สุดสำหรับ modular monolith source code ทั้งหมดของระบบอยู่ในที่เดียว จึงบำรุงรักษา ทดสอบ และ deploy ได้ง่ายกว่า อย่างไรก็ตาม จำเป็นต้องมี governance ที่เข้มงวด (ดู “Governance” ) เพื่อรักษาขอบเขตของแต่ละ module แม้ว่าตัวเลือกโครงสร้างนี้จะเรียบง่าย แต่นักพัฒนามีแนวโน้มที่จะใช้โค้ดซ้ำข้าม module มากเกินไป รวมถึงปล่อยให้มีการสื่อสารระหว่าง module มากเกินไปด้วย (ดู “Module Communication” ) แนวปฏิบัติเหล่านี้สามารถเปลี่ยน modular monolith ที่ออกแบบมาอย่างดีให้กลายเป็น Big Ball of Mud ที่ไร้โครงสร้างได้
Modular Structure
ด้วย modular structure module จะถูกแทนเป็น artifact ที่จบในตัวเอง (เช่นไฟล์ JAR และ DLL) แล้วนำมารวมกันเป็น deployment unit เดียวในขั้นตอนการ deploy Figure 11-3 แสดงตัวเลือกนี้โดยใช้ไฟล์ JAR ในแพลตฟอร์ม Java
Figure 11-3. ตัวอย่างของตัวเลือก modular structure โดยใช้ไฟล์ JAR
ข้อดีของโครงสร้างนี้คือแต่ละ module จบในตัวเอง ทำให้ทีมสามารถทำงานแยกกันในแต่ละ module ได้ (ดู “Team Topology Considerations” ) หลายครั้งอาจมี source code repository เฉพาะของทีมเองสำหรับ module เหล่านั้นด้วยซ้ำ ตัวเลือกนี้ทำงานได้ดีเมื่อ module ส่วนใหญ่เป็นอิสระจาก module อื่น มันยังทำงานได้ดีสำหรับระบบขนาดใหญ่และซับซ้อนกว่า ซึ่งแต่ละ module ต้องการความเชี่ยวชาญหรือความรู้ทางธุรกิจที่ต่างกัน ด้วยตัวเลือก modular structure นักพัฒนามักจะไม่ค่อยใช้โค้ดซ้ำมากเกินไป หรือทำให้ module สื่อสารกันมากเกินไป (ดู “Module Communication” ) ตัวเลือกนี้ยังมักจะสร้างขอบเขตที่สะอาดกว่าระหว่าง module และการแยก concern โดยรวมที่ดีกว่าด้วย
อย่างไรก็ตาม ตัวเลือกโครงสร้างนี้จะสูญเสียประสิทธิภาพเมื่อ module ที่พึ่งพากันจำเป็นต้องสื่อสารกัน ในกรณีนี้ แนวทาง monolithic structure จะมีประสิทธิภาพมากกว่า
Module Communication (การสื่อสารระหว่าง Module)
การสื่อสารระหว่าง module ไม่ใช่สิ่งที่ดีเลยใน architectural style นี้ แต่เราก็ยอมรับว่าในหลายกรณีมันจำเป็น ตัวอย่างเช่น ใน architecture ที่แสดงใน Figure 11-2 module OrderPlacement ต้องสื่อสารกับ module InventoryManagement เพื่อให้มันปรับปริมาณสินค้าคงคลังสำหรับสินค้าที่สั่งซื้อ และทำการประมวลผลเพิ่มเติมใด ๆ (เช่น สั่งสินค้าเพิ่มถ้าสินค้าคงคลังต่ำเกินไป) มันยังต้องสื่อสารกับ module PaymentProcessing เพื่อทำการชำระเงินสำหรับคำสั่งซื้อด้วย มีตัวเลือกหลักสองแบบสำหรับการสื่อสารระหว่าง module ซึ่งเราจะอธิบายในหัวข้อต่อไปนี้
Peer-to-peer approach (แนวทางแบบ Peer-to-Peer)
โซลูชันที่ตรงไปตรงมาที่สุดคือการสื่อสารแบบ peer-to-peer ง่าย ๆ ระหว่าง module ด้วยแนวทางนี้ class file ใน module หนึ่งจะสร้าง instance ของ class ใน module อื่น และเรียก method ที่จำเป็นใน class นั้นเพื่อทำการดำเนินการ (ดู Figure 11-4 )
Figure 11-4. การสื่อสารแบบ peer-to-peer ระหว่าง module
ปัญหาหนึ่งของแนวทาง monolithic คือมัน สะดวก เกินไปสำหรับนักพัฒนาที่จะสร้าง instance ของ class ใด ๆ ที่อยู่ภายใน module อื่น สิ่งนี้ทำให้ architecture ที่มีโครงสร้างดีกลายเป็น Big Ball of Mud antipattern ได้ง่าย (ดู Figure 9-1 )
อย่างไรก็ตาม ด้วย modular structure class ที่อยู่ใน module อื่นอาจอยู่ใน artifact ภายนอกที่แยกต่างหาก (ไฟล์ JAR หรือ DLL) แทนที่จะเป็น directory แยกต่างหากใน source code repository module ที่สื่อสารกับ module อื่นจะไม่สามารถ compile ได้ ถ้าไม่มี class reference ซึ่งหมายความว่านักพัฒนาต้องสร้าง dependency แบบ compile-time ระหว่าง module เหล่านั้น วิธีแก้ปัญหานี้ตามปกติคือการสร้าง shared interface class ระหว่าง module เหล่านั้น (ในไฟล์ JAR หรือ DLL ที่แชร์แยกต่างหาก) เพื่อให้แต่ละ module สามารถ compile ได้อย่างเป็นอิสระจาก module อื่น ไม่ว่าจะแบบไหน การสื่อสารระหว่าง module มากเกินไปด้วยแนวทาง modular structure จะนำไปสู่ DLL Hell antipattern (หรือใน Java platform คือ antipattern JAR Hell )
Mediator approach (แนวทางแบบ Mediator)
แนวทาง mediator จะ decouple module โดยใช้ mediator component เพื่อสร้าง abstraction layer ระหว่าง module mediator ทำหน้าที่เป็น orchestrator รับ request แล้วส่งต่อไปยัง module ที่เหมาะสม Figure 11-5 แสดงแนวทางนี้
Figure 11-5. mediator ทำการ decouple module ทำให้ module ไม่ต้องสื่อสารกันเองโดยตรง
ผู้อ่านที่ช่างสังเกตจะเห็นว่า แม้ mediator approach จะ decouple module แต่แต่ละ module ก็ยังคง coupled กับ mediator อยู่ดี แนวทางนี้ไม่ได้ขจัด coupling และ dependency ทั้งหมดออกไป แต่มันช่วยทำให้ architecture เรียบง่ายขึ้นและรักษาให้ module เป็นอิสระจากกันและกัน สังเกตว่าเป็น mediator ไม่ใช่ module ที่พึ่งพากัน ที่ต้องการ API หรือ interface บางอย่างเพื่อเรียกใช้ฟังก์ชันการทำงานใน module อื่น .