Pattern: Central Aggregating Gateway (แพตเทิร์น: Central Aggregating Gateway)
central-purpose aggregating gateway อยู่ระหว่างส่วนติดต่อผู้ใช้ภายนอกกับไมโครเซอร์วิสที่อยู่ปลายน้ำ และทำหน้าที่ filtering และ aggregation ของการเรียกสำหรับส่วนติดต่อผู้ใช้ทั้งหมด หากไม่มีการทำ aggregation ส่วนติดต่อผู้ใช้อาจต้องเรียกหลายครั้งเพื่อดึงข้อมูลที่ต้องการ ซึ่งมักทิ้งข้อมูลที่ดึงมาแต่ไม่ได้ใช้ไปอย่างสูญเปล่า
ใน Figure 14-8 เราจะเห็นสถานการณ์แบบนี้ เราต้องการแสดงหน้าจอที่มีข้อมูลเกี่ยวกับคำสั่งซื้อล่าสุดของลูกค้า หน้าจอนี้ต้องแสดงข้อมูลทั่วไปเกี่ยวกับลูกค้า แล้วก็แสดงรายการคำสั่งซื้อของพวกเขาเรียงตามวันที่ พร้อมข้อมูลสรุป แสดงวันที่และสถานะของแต่ละคำสั่งซื้อ รวมถึงราคาด้วย
Figure 14-8. การเรียกหลายครั้งเพื่อดึงข้อมูลสำหรับหน้าจอเดียว
เราเรียกตรงไปยัง microservice Customer ดึงข้อมูลทั้งหมดเกี่ยวกับลูกค้ากลับมา แม้ว่าเราจะต้องการแค่บาง field เท่านั้น จากนั้นเราก็ดึงรายละเอียดคำสั่งซื้อจาก microservice Order เราอาจปรับปรุงสถานการณ์นี้ได้บ้าง เช่น การเปลี่ยน microservice Customer หรือ Order ให้ส่งคืนข้อมูลที่เหมาะกับความต้องการเฉพาะกรณีนี้มากขึ้น แต่ก็ยังต้องเรียกสองครั้งอยู่ดี
ด้วย aggregating gateway เราสามารถส่งการเรียกเพียงครั้งเดียวจากส่วนติดต่อผู้ใช้ไปยัง gateway แทน จากนั้น aggregating gateway จะทำการเรียกที่จำเป็นทั้งหมด รวมผลลัพธ์เป็น response เดียว และทิ้งข้อมูลที่ส่วนติดต่อผู้ใช้ไม่ต้องการไป ( Figure 14-9 )
Figure 14-9. central gateway ฝั่งเซิร์ฟเวอร์จัดการ filtering และ aggregation ของการเรียกไปยังไมโครเซอร์วิสปลายน้ำ
gateway แบบนี้ยังช่วย batch การเรียกได้ด้วย ตัวอย่างเช่น แทนที่จะต้องค้นหา order ID 10 รายการผ่านการเรียกแยกกัน ผมสามารถส่ง batch request เดียวไปยัง aggregating gateway และให้มันจัดการส่วนที่เหลือได้
โดยพื้นฐานแล้ว การมี aggregating gateway บางรูปแบบสามารถลดจำนวนการเรียกที่ไคลเอนต์ภายนอกต้องทำ และลดปริมาณข้อมูลที่ต้องส่งกลับได้ ซึ่งสามารถนำไปสู่ประโยชน์ที่สำคัญในแง่ของการลดการใช้ bandwidth และปรับปรุง latency ของแอปพลิเคชัน
Ownership (ความเป็นเจ้าของ)
เมื่อส่วนติดต่อผู้ใช้จำนวนมากขึ้นใช้ central gateway และมีไมโครเซอร์วิสจำนวนมากขึ้นที่ต้องการ logic ของ call aggregation และ filtering สำหรับส่วนติดต่อผู้ใช้เหล่านั้น gateway ก็กลายเป็นแหล่งที่มาของการแย่งชิงที่อาจเกิดขึ้นได้ ใครเป็นเจ้าของ gateway กันแน่ เป็นของคนที่สร้างส่วนติดต่อผู้ใช้ หรือของคนที่เป็นเจ้าของไมโครเซอร์วิส บ่อยครั้งที่ผมพบว่า central aggregating gateway ทำอะไรมากมายจนสุดท้ายกลายเป็นของทีมเฉพาะทาง สวัสดีสถาปัตยกรรมแบบเลเยอร์ที่แบ่งไซโล!
โดยพื้นฐานแล้ว ลักษณะของ call aggregation และ filtering นั้นถูกขับเคลื่อนโดยความต้องการของส่วนติดต่อผู้ใช้ภายนอกเป็นหลัก ดังนั้นจึงสมเหตุสมผลตามธรรมชาติที่ gateway ควรเป็นของทีมที่สร้าง UI น่าเสียดายที่โดยเฉพาะในองค์กรที่มีทีม frontend เฉพาะทาง ทีมนั้นอาจไม่มีทักษะที่จะสร้าง backend component สำคัญขนาดนี้
ไม่ว่าใครจะเป็นเจ้าของ central gateway ในที่สุด มันก็มีศักยภาพที่จะกลายเป็น bottleneck สำหรับการส่งมอบ ถ้าหลายทีมต้องเปลี่ยนแปลง gateway การพัฒนาบนนั้นก็จะต้องอาศัยการประสานงานระหว่างทีมเหล่านั้น ทำให้ทุกอย่างช้าลง ถ้าทีมเดียวเป็นเจ้าของมัน ทีมนั้นก็อาจกลายเป็น bottleneck เมื่อพูดถึงการส่งมอบ เราจะดูว่า backend for frontend pattern ช่วยแก้ปัญหาเหล่านี้ได้อย่างไรในไม่ช้า
Different Types of User Interfaces (ส่วนติดต่อผู้ใช้ประเภทต่างๆ)
หากปัญหาเรื่องความเป็นเจ้าของสามารถจัดการได้ central aggregating gateway ก็อาจยังทำงานได้ดี จนกว่าเราจะพิจารณาประเด็นเรื่องอุปกรณ์ที่แตกต่างกันและความต้องการที่แตกต่างกันของมัน อย่างที่เราคุยกันไปแล้ว คุณลักษณะของอุปกรณ์มือถือแตกต่างกันมาก เรามีพื้นที่หน้าจอน้อยกว่า ซึ่งหมายความว่าเราแสดงข้อมูลได้น้อยกว่า การเปิด connection จำนวนมากไปยังทรัพยากรฝั่งเซิร์ฟเวอร์สามารถทำให้แบตเตอรี่หมดและใช้แพ็กเกจข้อมูลที่จำกัด นอกจากนี้ ลักษณะของการโต้ตอบที่เราต้องการมอบให้บนอุปกรณ์มือถือก็อาจแตกต่างกันอย่างมาก ลองนึกถึงร้านค้าปลีกแบบมีหน้าร้านทั่วไป บนแอปเดสก์ท็อป ผมอาจให้คุณดูสินค้าที่วางขายและสั่งซื้อออนไลน์หรือจองไว้ในร้าน แต่บนอุปกรณ์มือถือ ผมอาจต้องการให้คุณสแกนบาร์โค้ดเพื่อเปรียบเทียบราคา หรือมอบข้อเสนอตาม context ให้ในขณะที่คุณอยู่ในร้าน เมื่อเราสร้างแอปพลิเคชันมือถือมากขึ้นเรื่อยๆ เราก็เริ่มตระหนักว่าผู้คนใช้งานมันแตกต่างกันมาก และด้วยเหตุนี้ functionality ที่เราต้องเปิดให้ใช้ก็จะแตกต่างกันไปด้วย
ดังนั้นในทางปฏิบัติ อุปกรณ์มือถือของเราจะต้องการเรียกในแบบที่แตกต่างและน้อยกว่า และต้องการแสดงข้อมูลที่แตกต่าง (และน่าจะน้อยกว่า) เมื่อเทียบกับคู่ของมันบนเดสก์ท็อป นี่หมายความว่าเราต้องเพิ่ม functionality เข้าไปใน API backend ของเราเพื่อรองรับส่วนติดต่อผู้ใช้ประเภทต่างๆ ใน Figure 14-10 เราจะเห็น web interface และ mobile interface ของ MusicCorp ที่ใช้ gateway เดียวกันสำหรับหน้าจอสรุปข้อมูลลูกค้า แต่แต่ละไคลเอนต์ต้องการชุดข้อมูลที่ต่างกัน web interface ต้องการข้อมูลเกี่ยวกับลูกค้ามากกว่า และยังมีสรุปย่อของสินค้าในแต่ละคำสั่งซื้อด้วย สิ่งนี้ทำให้เราต้อง implement การเรียกแบบ aggregating และ filtering สองแบบที่แตกต่างกันใน backend gateway ของเรา
Figure 14-10. การรองรับการเรียก aggregating ที่ต่างกันสำหรับอุปกรณ์ที่ต่างกัน
สิ่งนี้อาจนำไปสู่ความบวมใน gateway อย่างมาก โดยเฉพาะถ้าเราพิจารณาแอปพลิเคชันมือถือ native ที่ต่างกัน เว็บไซต์ที่ลูกค้าเห็น interface ด้านการบริหารภายใน และอื่นๆ เรายังมีปัญหาด้วยว่า แม้ UI ต่างๆ เหล่านี้อาจเป็นของทีมที่ต่างกัน แต่ gateway เป็นหน่วยเดียว เรามีปัญหาแบบเดิมที่หลายทีมต้องทำงานบน deployed unit เดียวกัน aggregating backend เดียวของเราอาจกลายเป็น bottleneck ได้ เพราะมีการเปลี่ยนแปลงจำนวนมากที่พยายามทำกับ deployable artifact เดียวกัน
Multiple Concerns (ประเด็นที่หลากหลาย)
มีประเด็นหลากหลายที่อาจต้องได้รับการแก้ไขฝั่งเซิร์ฟเวอร์เมื่อพูดถึงการจัดการ API call นอกจาก call aggregation และ filtering แล้ว เรายังนึกถึงประเด็นทั่วไปกว่า เช่น การจัดการ API key การยืนยันตัวตนผู้ใช้ หรือ call routing บ่อยครั้งที่ประเด็นทั่วไปเหล่านี้สามารถจัดการได้โดยผลิตภัณฑ์ API gateway ซึ่งมีให้เลือกหลายขนาดและหลายช่วงราคา (บางตัวก็แพงจนตาลาย!) ขึ้นอยู่กับความซับซ้อนที่คุณต้องการ มันอาจสมเหตุสมผลมากที่จะซื้อผลิตภัณฑ์ (หรือ license บริการ) มาจัดการประเด็นเหล่านี้ให้คุณ คุณอยากจัดการเรื่อง API key issuing, tracking, rate limiting และอื่นๆ ด้วยตัวเองจริงๆ หรือ ลองมองหาผลิตภัณฑ์ในพื้นที่นี้เพื่อแก้ปัญหาทั่วไปเหล่านี้ได้ตามสบาย แต่ก็ต้องระวังการใช้ผลิตภัณฑ์เหล่านี้ทำ call aggregation และ filtering ด้วย แม้พวกเขาจะอ้างว่าทำได้ก็ตาม
เมื่อคุณ customize ผลิตภัณฑ์ที่คนอื่นสร้าง คุณมักต้องทำงานในโลกของพวกเขา toolchain ของคุณถูกจำกัด เพราะคุณอาจไม่สามารถใช้ภาษาโปรแกรมมิ่งและวิธีปฏิบัติในการพัฒนาของคุณเองได้ แทนที่จะเขียนโค้ด Java คุณกำลัง config routing rule ใน DSL เฉพาะของผลิตภัณฑ์แปลกๆ (อาจใช้ JSON) มันอาจเป็นประสบการณ์ที่น่าหงุดหงิด และคุณก็กำลังฝัง smart บางอย่างของระบบคุณไว้ในผลิตภัณฑ์ของ third party นี่สามารถลดความสามารถของคุณในการย้ายพฤติกรรมนี้ในภายหลังได้ เป็นเรื่องปกติที่จะพบว่าแพตเทิร์นของ call aggregation จริงๆ แล้วเกี่ยวข้องกับ domain functionality บางอย่างที่อาจสมควรมี microservice ของตัวเอง (สิ่งที่เราจะสำรวจเพิ่มเติมในไม่ช้าเมื่อคุยเรื่อง BFF) ถ้าพฤติกรรมนี้อยู่ใน configuration เฉพาะของ vendor การย้าย functionality นี้ก็อาจมีปัญหามากขึ้น เพราะคุณอาจต้องคิดค้นมันขึ้นมาใหม่
สถานการณ์อาจแย่ลงไปอีกถ้า aggregating gateway ซับซ้อนมากพอที่จะต้องมีทีมเฉพาะทางเป็นเจ้าของและดูแลมัน ในกรณีที่แย่ที่สุด การใช้ความเป็นเจ้าของแบบแนวนอนมากขึ้นอาจนำไปสู่สถานการณ์ที่การจะ roll out functionality ใหม่ คุณต้องให้ทีม frontend ทำการเปลี่ยนแปลง ทีม aggregating gateway ก็ต้องทำการเปลี่ยนแปลง และทีมที่เป็นเจ้าของ microservice ก็ต้องทำการเปลี่ยนแปลงของตัวเองด้วย ทันใดนั้นทุกอย่างก็เริ่มช้าลงไปมาก
ดังนั้นถ้าคุณต้องการใช้ API gateway เฉพาะทาง ก็เชิญเลย แต่ควรพิจารณาอย่างจริงจังที่จะให้ logic ของการ filtering และ aggregation ของคุณอยู่ที่อื่น
When to Use It (เมื่อไรควรใช้)
สำหรับ solution ที่เป็นของทีมเดียว ที่ทีมเดียวพัฒนาทั้งส่วนติดต่อผู้ใช้และไมโครเซอร์วิสฝั่ง backend ผมโอเคที่จะมี central aggregating gateway เดียว แต่ทีมนั้นก็ดูเหมือนจะทำงานเยอะมาก ในสถานการณ์แบบนี้ ผมมักเห็นความสอดคล้องกันในระดับสูงข้ามส่วนติดต่อผู้ใช้ ซึ่งมักตัดความจำเป็นของจุด aggregation เหล่านี้ไปตั้งแต่แรกอยู่แล้ว
ถ้าคุณตัดสินใจใช้ central aggregating gateway เดียว โปรดระมัดระวังในการจำกัด functionality ที่คุณใส่เข้าไปในนั้น ผมจะระวังอย่างมากในการผลัก functionality นี้เข้าไปในผลิตภัณฑ์ API gateway ทั่วไปมากขึ้น ด้วยเหตุผลที่กล่าวไปก่อนหน้านี้
แนวคิดของการทำ call filtering และ aggregation บางรูปแบบฝั่ง backend สามารถสำคัญได้จริงๆ ในแง่ของการ optimize ประสบการณ์ผู้ใช้ของส่วนติดต่อผู้ใช้ของเรา ปัญหาคือในองค์กรที่มีการส่งมอบด้วยหลายทีม central gateway อาจนำไปสู่ความจำเป็นในการประสานงานจำนวนมากระหว่างทีมเหล่านั้น
ดังนั้นถ้าเรายังต้องการทำ aggregation และ filtering ฝั่ง backend แต่ต้องการกำจัดปัญหาที่เกี่ยวข้องกับโมเดลความเป็นเจ้าของของ central gateway เราจะทำอย่างไรได้บ้าง นี่คือจุดที่แพตเทิร์น backend for frontend เข้ามา