Reuse (การนำกลับมาใช้ใหม่)

การแบ่งแยกระหว่าง domain coupling กับ operational coupling เป็นประเด็นด้าน architecture ที่พบได้ทั่วไปใน distributed architecture อย่าง microservices

Separating Domain and Operational Coupling (การแยก Domain Coupling ออกจาก Operational Coupling)

เป้าหมายหนึ่งของการออกแบบ microservices architecture คือ decoupling ในระดับสูง ซึ่งมักสะท้อนออกมาในคำแนะนำ ที่ว่า “การ duplicate ดีกว่าการ couple”

สมมติว่าสอง service จำเป็นต้องส่งข้อมูล customer profile ไปมาระหว่างกัน แต่ domain-driven bounded context ของ architecture ยืนกรานว่ารายละเอียดการ implement ต้องเป็นเรื่องส่วนตัวของแต่ละ service เท่านั้น solution ทั่วไปคือให้แต่ละ service มี internal representation ของ entity อย่าง Profile เป็นของตัวเอง แล้วส่งข้อมูลนั้นด้วยวิธีที่ coupling หลวม เช่น name-value pair แบบ JSON วิธีนี้ทำให้แต่ละ service เปลี่ยน internal representation ของตัวเองได้ตามใจชอบ—รวมถึง technology stack ด้วย—โดยไม่ทำให้ การ integration พัง นักพัฒนาโดยทั่วไปมักไม่ชอบการ duplicate code เพราะอาจก่อปัญหาเรื่อง synchronization, semantic drift และอื่น ๆ อีกมาก แต่บางอย่างก็แย่กว่าการ duplicate…และใน microservices นั้น coupling คือหนึ่งในนั้น

สถาปนิกที่ออกแบบ microservices มักต้องยอมรับความจริงที่ว่าบางครั้งก็ต้อง duplicate implementation เพื่อรักษา decoupling ไว้ แต่แล้วความสามารถที่ ได้ประโยชน์ จาก coupling ระดับสูงล่ะ? แต่ละ service ควรมีความสามารถด้าน operational ร่วมกันบางอย่าง เช่น monitoring, logging, authentication และ authorization และ circuit breaker อย่างไรก็ตาม การปล่อยให้แต่ละทีมจัดการ dependency เหล่านี้เองมักลงเอยด้วยความวุ่นวาย

ตัวอย่างเช่น ลองนึกถึงบริษัทที่พยายามเลือก monitoring solution มาตรฐานเดียวสำหรับทุก service เพื่อให้ operationalize มันได้ง่ายขึ้น สถาปนิกตัดสินใจให้แต่ละทีมรับผิดชอบการ implement monitoring ให้ service ของตัวเอง เช่น ทีม Payment service, ทีม Inventory service และอื่น ๆ แต่ทีม operations จะมั่นใจได้อย่างไรว่าแต่ละทีมทำจริง? แล้วเรื่องการ upgrade ที่ต้องเป็นแบบรวมศูนย์ล่ะ? ถ้า monitoring tool มาตรฐานต้อง upgrade ทั้งองค์กร แต่ละทีมจะประสาน งานกันอย่างไร?

Hexagonal architecture (สถาปัตยกรรมแบบ Hexagonal)

ใน architecture pattern แบบ Hexagonal ซึ่งแสดงใน Figure 20-1 domain logic จะอยู่ตรงกลางของ hexagon ซึ่งถูกล้อมรอบด้วย port และ adapter ที่เชื่อมต่อไปยังส่วนอื่น ๆ ของ ecosystem (อันที่จริง pattern นี้ยังถูกเรียกอีกชื่อว่า Ports and Adapters pattern) ภาพประกอบของ pattern นี้แสดงอยู่ใน Figure 20-1

The Hexagonal architecture pattern

Figure 20-1. The Hexagonal architecture pattern

ผู้อ่านที่ช่างสังเกตอาจจะสังเกตเห็นว่ามีแค่สี่ในหกด้านของ hexagon เท่านั้นที่ถูกใช้งาน ผู้คิดค้น pattern นี้ Alistair Cockburn วาดมันเป็นรูป hexagon ตั้งแต่แรกและเรียกมันว่า Hexagonal architecture pattern แล้วก็มาเสียใจภายหลังเกือบจะทันที เพราะชื่อ Ports and Adapters สื่อความหมายได้ ตรงกว่า แต่ก็สายไปแล้ว สถาปนิกจำนวนมากรู้สึกว่าชื่อ “Hexagonal” ฟังดูเท่ ชื่อนี้จึงติดปากมาจนถึงทุกวันนี้

การเข้าใจผิดว่า pattern คือ implementation เป็นความเสี่ยงที่พบได้บ่อย และนี่คือตัวอย่างที่ดี Hexagonal architecture มีจุดอ่อนที่ค่อนข้างร้ายแรงเมื่อถูกใช้อธิบาย microservices แต่จะเห็นได้เฉพาะคนที่เข้าใจเจตนาดั้งเดิมของ pattern นี้จริง ๆ เท่านั้น Hexagonal architecture เกิดขึ้นก่อน microservices สมัยใหม่ แต่ทั้งสองก็มีความคล้ายคลึงกันหลายอย่าง และก็มีความแตกต่างสำคัญ อยู่หนึ่งอย่างด้วย นั่นคือ data fidelity Hexagonal architecture มองว่าฐานข้อมูลเป็นเพียง adapter อีกตัวหนึ่งที่เสียบเข้ามาได้ มันไม่ได้รวม data schema เข้าไว้เป็นส่วนหนึ่งของ business logic เพราะ ความเข้าใจผิด (ซึ่งพบได้ทั่วไปในยุคที่ชื่อ Hexagonal pattern ถูกบัญญัติขึ้น) ที่ว่าฐานข้อมูลเป็นเครื่อง จักรอีกชิ้นหนึ่งที่แยกออกไปต่างหาก Eric Evans มีความเข้าใจที่ลึกซึ้งพอจะแก้ไขความเข้าใจผิดนี้ในหนังสือ ของเขาเรื่อง Domain-Driven Design โดยชี้ให้เห็นว่า ไม่ว่า database schema จะอยู่ที่ไหนก็ตาม มันก็ต้องเปลี่ยนแปลงไปตาม business logic ของระบบเสมอ

สิ่งนี้ทำให้ Hexagonal pattern เป็นแหล่งความสับสนตลอดกาลในหมู่สถาปนิก เพราะเวลาใครใช้คำนี้ เขากำลังพูดถึงการแยก operational concern ออกจาก domain concern หรือกำลังหมายถึง pattern ตามตัวอักษรจริง ๆ ที่จะแยก data ออกไปต่างหาก ซึ่งขัดกับหลักการออกแบบ microservices หลักข้อหนึ่งกันแน่? การใช้ชื่อ pattern เป็นคำย่อแทนความหมาย “การแยก domain กับ operational concern ออกจากกัน” นั้นก็โอเค ตราบใดที่ไม่ทำให้ เข้าใจผิดในบริบทนั้น ๆ อย่างไรก็ตาม สถาปนิกในปัจจุบันไม่จำเป็นต้องใช้ implementation แบบนี้แล้ว เพราะตอนนี้เรามีกลไกที่เหมาะสมกว่าในการ implement Hexagonal pattern นั่นคือ Service Mesh pattern

Service Mesh (Service Mesh)

ย้อนกลับไปที่ “Operational Reuse” เราได้อธิบาย architectural approach ทั่วไปในการแยก technical concern ออกจาก domain concern ไว้แล้ว โดยใช้ Sidecar และ Service Mesh pattern

Sidecar pattern ไม่ได้เป็นแค่วิธี decouple operational capability ออกจาก domain เท่านั้น—มันยังเป็น orthogonal reuse pattern เพื่อจัดการ coupling ประเภทหนึ่งโดยเฉพาะด้วย (ดู “Orthogonal Coupling” ) หลายครั้ง architectural solution ต้องการ coupling หลายประเภทพร้อมกัน เช่นตัวอย่างของเราตอนนี้ที่เป็น domain coupling เทียบกับ operational coupling Orthogonal Reuse pattern นำเสนอวิธี reuse บางแง่มุมที่ concern หนึ่งหรือหลาย concern ใน architecture เป็นตัวแทนอยู่ ซึ่งไม่เข้ากับการจัดระเบียบแบบลำดับชั้นที่ต้องการ เช่น microservices architecture ถูกจัดระเบียบตาม domain แต่ operational coupling กลับต้องตัดข้าม domain เหล่านั้น sidecar ช่วยให้สถาปนิกแยก concern เหล่านั้นไว้ใน layer ที่ตัดข้ามกันแต่มีความสม่ำเสมอตลอดทั้ง architecture