เราจะแยกตรรกะทางธุรกิจจากการเรียก API ได้อย่างไร
แรงจูงใจและภาพรวม
เป้าหมายของเราคือการทำให้คำขอ API บางอย่างเป็นแบบอัตโนมัติโดยใช้ภาษาการสืบค้นเฉพาะ แทนที่จะส่งคำขอ API โดยตรง เราจะส่งแบบสอบถามที่มีการเรียกเมธอด แต่ละวิธีสามารถจัดการได้โดยตรงหรือมอบหมายไปยังอินสแตนซ์อื่น หากมีการมอบหมายวิธีการ ส่วนของแบบสอบถามซึ่งมีการเรียกวิธีการจะถูกส่งไปยังอินสแตนซ์อื่น คุณลักษณะสำคัญของแนวทางนี้คือความสามารถในการแยกแบบสอบถามออกเป็นส่วนๆ และดำเนินการแต่ละส่วนแยกกันเช่นเดียวกับแบบสอบถามอื่นๆ แต่ละชิ้นดังกล่าวจะได้รับตัวแก้ไขหนึ่งตัว ตัวแก้ไขจะถูกกำหนดให้กับชิ้นส่วนโดยอัตโนมัติตามวิธีการที่ได้รับมอบสิทธิ์ แบบสอบถามยังสามารถประกอบด้วยการกำหนดตัวแปร เงื่อนไข และคำสั่ง return ซึ่งช่วยให้เราใช้ตรรกะพื้นฐานในการสืบค้นได้ เราได้พัฒนาข้อพิสูจน์แนวคิดเกี่ยวกับ JavaScript ไลบรารีนี้เรียกว่า limboและจะพร้อมใช้งานใน npm
ข้อความค้นหาเหล่านั้นมีลักษณะอย่างไร
แบบสอบถามประกอบด้วยบรรทัด แต่ละบรรทัดควรลงท้ายด้วย ;
บรรทัดสามารถดำเนินการข้อมูลโดยใช้ชุดตัวดำเนินการที่กำหนดไว้ ข้อมูลสามารถนำเสนอเป็นข้อมูลพื้นฐาน (สตริง ตัวเลข บูลีน) อาร์เรย์ หรืออ็อบเจ็กต์ เพื่อกำหนดอาร์เรย์และวัตถุ เราใช้รูปแบบ JSON โอเปอเรเตอร์ถัดไปจะพร้อมใช้งาน:
- การเรียกวิธีการ ที่จริงแล้วการเรียกเมธอดสามารถส่งพารามิเตอร์ได้หนึ่งตัว พารามิเตอร์นี้สามารถเป็นสตริง บูลีน ตัวเลข อาร์เรย์ หรือวัตถุ
methodName ~ {"key" : "val"};
- เงื่อนไข. ดำเนินการแบบสอบถามย่อยตามเงื่อนไข ข้อความค้นหาย่อยควรขึ้นต้นด้วย
@{;
และลงท้ายด้วย};
? $varName == "value" @{;
$result = method ~ $varName;
} : @{;
$result = method2 ~ $varName;
}; - กำหนด. การกำหนดค่าให้กับตัวแปร นั่นคือวิธีที่เราสามารถบันทึกข้อมูลเพื่อใช้ต่อไปได้ ชื่อตัวแปรแต่ละตัวควรขึ้นต้นด้วย “$”
$varName = "value"
- ส่งคืนค่า สิ้นสุดแบบสอบถาม ส่งกลับค่า
=>$reuslt;
ตัวดำเนินการภายในบรรทัดจะถูกดำเนินการตามลำดับที่แสดงด้านบน เราสามารถใช้วงเล็บ ()
เพื่อเปลี่ยนลำดับได้ นอกจากนี้ เราควรใช้วงเล็บในขณะที่เรียกตัวดำเนินการในการกำหนดวัตถุ JSON: =>{"key" : (method ~ "val")};
ตัวอย่าง:
$result = method ~ {"key" : "val"}; ? $result.success == true @{; =>$result; } : @{; writeErrorLog ~ $result.error; => { "success" : false, "error" : "Error during calling /"method/"" }; };
วิธีการจัดการและการมอบหมาย ดำเนินการสอบถาม
อินสแตนซ์หนึ่งสามารถจัดการวิธีการบางอย่างและมอบหมายวิธีอื่นได้ ถ้าวิธีการได้รับมอบสิทธิ์ การโทรกลับจะได้รับชิ้นส่วนตามวิธีการที่ได้รับมอบสิทธิ์ มิฉะนั้น การโทรกลับจะได้รับเฉพาะพารามิเตอร์ที่กำหนดไว้ในการเรียกเมธอดและชื่อเมธอด
กำลังมอบหมาย
เพื่อมอบหมายวิธีการเราควรเรียกวิธีการ "มอบหมาย" ของอินสแตนซ์
ผู้รับมอบสิทธิ์(ตัวเลือก : วัตถุ) : เป็นโมฆะ
options.regExp : RegExp วิธีการที่ตรงกับ regExp จะได้รับการมอบหมาย
options.handle : Function(query : String) ฟังก์ชันเรียกกลับ
การจัดการ
หากเราต้องการจัดการเมธอดตามอินสแตนซ์ปัจจุบัน เราควรเรียกเมธอด addHandler ของอินสแตนซ์
addHandler(options : object) : void
options.regExp : RegExp วิธีการที่ตรงกับ regExp จะถูกประมวลผลโดยตัวจัดการปัจจุบัน
options.handle : Function(param, methodName) ฟังก์ชันโทรกลับ
addHandlers(Handlers : Array) : void
เพิ่มตัวจัดการหลายตัว
ดำเนินการสอบถาม
เพื่อดำเนินการค้นหา เราเรียกวิธีการ "เรียก" ของอินสแตนซ์
call(query : String) : Promise
query : String แบบสอบถามที่เราต้องการดำเนินการ
ลำดับการดำเนินการแบบสอบถาม
ก่อนดำเนินการ อัลกอริธึมจะเรียกใช้แบบสอบถามและกำหนดว่าส่วนใดควรดำเนินการจริงโดยอินสแตนซ์ปัจจุบัน และส่วนใดที่ควรมอบหมายให้กับอินสแตนซ์อื่น กระบวนการนี้ประกอบด้วยขั้นตอนถัดไป:
- สร้างก้อน
- ดำเนินการก้อนข้อมูลในเครื่องหรือส่งไปยังอินสแตนซ์อื่น
- ผลลัพธ์ของกระบวนการ
- ไปที่ข้อ 1 หากไม่เสร็จสิ้น
การแยกและการกำหนดตัวแก้ไข
ในระหว่างการประมวลผล แบบสอบถามจะถูกแบ่งออกเป็นส่วน ๆ ตามวิธีการที่ได้รับมอบสิทธิ์ ในขณะที่แยก เราพยายามลดจำนวนชิ้นข้อมูลและส่งผลให้จำนวนคำขอ API ที่เป็นไปได้ เนื่องจากการสืบค้นอาจมีเงื่อนไข อาจมีบล็อกที่ซ้อนกันและความจำเป็นในการจัดการกรณีต่างๆ เมื่อชิ้นส่วนสิ้นสุดในบล็อกที่ซ้อนกันดังกล่าว เพื่อจัดการกับความท้าทายนี้ เราได้เพิ่มโอเปอเรเตอร์อื่น ->@_bookmark
ซึ่งกลับมาที่บุ๊กมาร์ก ตัวดำเนินการนี้จะถูกวางโดยอัตโนมัติในระหว่างการแยกชิ้นส่วน หากชิ้นปัจจุบันส่งคืนบุ๊กมาร์ก การเรียกใช้โค้ดเพิ่มเติมควรเริ่มต้นจากบรรทัดที่ระบุในบุ๊กมาร์ก ตัวอย่างเช่น:
$res1 = resolver1.method1 ~ "string"; ? $res1.success == true @{; resolver1.method2 ~ "anotherString"; $res2 = resolver2.method1 ~ $res1; ? res2.success @{; resolver1.method3 ~ $res2; }; =>resolver2.method2 ~ $res2; }; =>resolver1.method4 ~ $res1;
สำหรับแบบสอบถามดังกล่าว solver1 จะได้รับถัดไป:
$res1 = resolver1.method1 ~ "string"; ? $res1.success == true @{; resolver1.method2 ~ "anotherString"; ->@_0; }; =>resolver1.method3 ~ $res1;
จากนั้นถ้า $res1.success จะเป็น false solver2 จะไม่ถูกเรียก มิฉะนั้นจะได้รับ:
$res2 = resolver2.method1 ~ $res1; ? res2.success == true @{; ->@_1; }; =>resolver2.method2 ~ $res2;
และถ้า $res2.success เป็นเท็จ solver1 จะได้รับการเรียกครั้งสุดท้าย:
resolver1.method3 ~ $res2; =>resolver1.method4 ~ $res1;
$res1 และ $res2 จะถูกแทนที่ด้วยข้อมูลจริง
อัลกอริธึมการกำหนดการแยกแบบสอบถามและตัวแก้ไขทั้งหมดมีลักษณะดังนี้:
กรณีการใช้งาน
- เรามีไคลเอนต์ที่โต้ตอบกับ API หลายตัว API เหล่านั้นจะแสดงเป็นจุดสิ้นสุด REST และ/หรือ GraphQL ในกรณีนี้ ลูกค้าของเราใช้ Limbo เป็นจุดเดียวในการรับและจัดการข้อมูลจาก API เหล่านั้น
2. เรามีเซิร์ฟเวอร์ที่รับคำถามและประมวลผล
กรณีการใช้งานเหล่านั้นสามารถนำมารวมกันได้ ลูกค้ารายหนึ่งสามารถเรียก API ที่ใช้ Limbo และเรียก API ที่ไม่ได้ใช้งาน ในเวลาเดียวกันเซิร์ฟเวอร์สามารถเรียกใช้ API อื่น ๆ และมอบหมายการสืบค้นให้กับพวกเขาได้
ข้อสรุป
วิธีการดังกล่าวอาจเหมาะสมอย่างยิ่งสำหรับสถาปัตยกรรมไมโครเซอร์วิส เนื่องจากเป็นการแยกตรรกะทางธุรกิจจากการเรียก API และอนุญาตให้ดำเนินการแบบสอบถามเดียวโดยหลายอินสแตนซ์ตามความต้องการ ไม่ควรเปลี่ยนแปลงการสืบค้นเมื่อบริการช่วยให้โซลูชันสามารถปรับขนาดได้
เนื่องจากเป็นเพียงการพิสูจน์แนวคิด จึงยังมีช่องว่างสำหรับการปรับปรุง เช่น:
- ลูป ขณะนี้ภาษาของแบบสอบถามไม่รองรับการวนซ้ำ แต่จะได้รับการแก้ไขเร็วๆ นี้
- โฟลว์การดำเนินการแบบสอบถามแบบอะซิงโครนัส เนื่องจากนั่นเป็นเพียงการพิสูจน์แนวคิดเท่านั้น การไหลแบบอะซิงโครนัสจึงไม่เหมาะสมที่สุดทั้งหมด (บรรทัดจะถูกดำเนินการทีละรายการแม้ว่าจะเป็นอิสระก็ตาม) เราจะอนุญาตให้มีการดำเนินการแบบขนานของหลายบรรทัดหรือหลายชิ้น
อย่างไรก็ตาม ห้องสมุดก็พร้อมใช้งานและในไม่ช้าก็จะมีความแข็งแกร่งมากขึ้น แนวทางนี้มีศักยภาพในการปรับปรุงกระบวนการพัฒนาไมโครเซอร์วิส