หลักการพัฒนาแอพพลิเคชั่นสมัยใหม่จาก NGINX ส่วนที่ 1

สวัสดีเพื่อน. ระหว่างรอเปิดคอร์ส นักพัฒนาแบ็กเอนด์ PHPตามเนื้อผ้าแบ่งปันการแปลเนื้อหาที่มีประโยชน์กับคุณ

ซอฟต์แวร์ช่วยแก้ปัญหางานประจำวันที่มากขึ้นในขณะที่มีความซับซ้อนมากขึ้นเรื่อยๆ ดังที่ Marc Andressen เคยกล่าวไว้ มันกินโลก

หลักการพัฒนาแอพพลิเคชั่นสมัยใหม่จาก NGINX ส่วนที่ 1

ด้วยเหตุนี้ วิธีการพัฒนาและส่งมอบแอปพลิเคชันจึงเปลี่ยนไปอย่างมากในช่วงไม่กี่ปีที่ผ่านมา สิ่งเหล่านี้คือการเปลี่ยนแปลงของระดับเปลือกโลกที่ส่งผลให้เกิดหลักการชุดหนึ่ง หลักการเหล่านี้ได้รับการพิสูจน์แล้วว่ามีประโยชน์ในการสร้างทีม ออกแบบ พัฒนา และส่งมอบแอปพลิเคชันของคุณไปยังผู้ใช้ปลายทาง

สรุปหลักการได้ดังนี้ แอปพลิเคชันควรมีขนาดเล็ก ทำงานบนเว็บ และมีสถาปัตยกรรมที่เน้นนักพัฒนาเป็นศูนย์กลาง. เมื่อคำนึงถึงหลักการสามประการนี้ คุณจะสามารถสร้างแอปพลิเคชันแบบครบวงจรที่มีประสิทธิภาพ ซึ่งสามารถจัดส่งไปยังผู้ใช้ปลายทางได้อย่างรวดเร็วและปลอดภัย อีกทั้งยังปรับขนาดและขยายได้อย่างง่ายดาย

หลักการพัฒนาแอพพลิเคชั่นสมัยใหม่จาก NGINX ส่วนที่ 1

หลักการที่เสนอแต่ละข้อมีหลายแง่มุมที่เราจะหารือเพื่อแสดงให้เห็นว่าหลักการแต่ละข้อมีส่วนช่วยให้บรรลุเป้าหมายสูงสุดอย่างไร ซึ่งก็คือการส่งมอบแอปพลิเคชันที่เชื่อถือได้ที่รวดเร็วซึ่งง่ายต่อการบำรุงรักษาและใช้งาน เราจะดูหลักการที่เกี่ยวข้องกับสิ่งที่ตรงกันข้ามเพื่ออธิบายความหมายของมันให้ชัดเจน เช่น "จงแน่ใจว่าคุณใช้ หลักความเล็ก'

เราหวังว่าบทความนี้จะสนับสนุนให้คุณใช้หลักการที่เสนอสำหรับการสร้างแอปพลิเคชันสมัยใหม่ ซึ่งจะเป็นแนวทางที่รวมเป็นหนึ่งในการออกแบบในบริบทของกองเทคโนโลยีที่เติบโตอย่างต่อเนื่อง

เมื่อใช้หลักการเหล่านี้ คุณจะพบว่าตัวเองกำลังใช้ประโยชน์จากแนวโน้มล่าสุดในการพัฒนาซอฟต์แวร์ รวมถึง DevOps ไปจนถึงการพัฒนาและส่งมอบแอพพลิเคชั่น การใช้คอนเทนเนอร์ (เช่น นักเทียบท่า) และเฟรมเวิร์กการประสานคอนเทนเนอร์ (ตัวอย่างเช่น Kubernetes) การใช้ไมโครเซอร์วิส (รวมถึงสถาปัตยกรรมไมโครเซอร์วิส NGINX и สถาปัตยกรรมการสื่อสารเครือข่าย สำหรับแอปพลิเคชันไมโครเซอร์วิส

แอปพลิเคชั่นสมัยใหม่คืออะไร?

แอพพลิเคชั่นสมัยใหม่? โมเดิร์นสแต็ค? "ทันสมัย" หมายถึงอะไรกันแน่?

นักพัฒนาส่วนใหญ่มีเพียงแนวคิดทั่วไปว่าแอปพลิเคชันสมัยใหม่ประกอบด้วยอะไร ดังนั้นจึงจำเป็นต้องกำหนดแนวคิดนี้ให้ชัดเจน

แอปสมัยใหม่รองรับไคลเอ็นต์หลายตัว ไม่ว่าจะเป็นอินเทอร์เฟซผู้ใช้ไลบรารี React JavaScript, แอปมือถือ Android หรือ iOS หรือแอปที่เชื่อมต่อกับ API อื่น แอปพลิเคชันสมัยใหม่หมายถึงจำนวนลูกค้าที่ให้ข้อมูลหรือบริการที่ไม่แน่นอน

แอปพลิเคชันสมัยใหม่มี API เพื่อเข้าถึงข้อมูลและบริการที่ร้องขอ API ควรไม่เปลี่ยนรูปและคงที่ และไม่ได้เขียนขึ้นโดยเฉพาะสำหรับคำขอเฉพาะจากไคลเอ็นต์เฉพาะ API พร้อมใช้งานผ่าน HTTP(S) และให้การเข้าถึงฟังก์ชันทั้งหมดที่มีใน GUI หรือ CLI

ข้อมูลต้องอยู่ในรูปแบบที่ยอมรับโดยทั่วไปและทำงานร่วมกันได้ เช่น JSON API เปิดเผยออบเจกต์และบริการในลักษณะที่สะอาดและเป็นระเบียบ เช่น RESTful API หรือ GraphQL มีอินเทอร์เฟซที่เหมาะสม

แอปพลิเคชันสมัยใหม่สร้างขึ้นบนสแต็กสมัยใหม่ และสแต็กสมัยใหม่คือสแต็กที่รองรับแอปพลิเคชันดังกล่าวตามลำดับ สแต็กนี้ช่วยให้นักพัฒนาสร้างแอปพลิเคชันได้อย่างง่ายดายด้วยอินเทอร์เฟซ HTTP และล้างตำแหน่งข้อมูล API วิธีการที่เลือกจะช่วยให้แอปพลิเคชันของคุณสามารถรับและส่งข้อมูลในรูปแบบ JSON ได้อย่างง่ายดาย กล่าวอีกนัยหนึ่ง Modern Stack สอดคล้องกับองค์ประกอบของแอปพลิเคชัน Twelve-Factor สำหรับ ไมโครเซอร์วิส.

เวอร์ชันยอดนิยมของสแต็กประเภทนี้อิงตาม ชวา, หลาม, โหนด, ทับทิม, PHP и Go. สถาปัตยกรรมไมโครเซอร์วิส NGINX แสดงตัวอย่างสแต็กสมัยใหม่ที่ใช้ในแต่ละภาษาที่กล่าวถึง

โปรดทราบว่าเราไม่ได้สนับสนุนวิธีการไมโครเซอร์วิสเพียงอย่างเดียว พวกคุณหลายคนกำลังทำงานกับ monoliths ที่ต้องมีวิวัฒนาการ ขณะที่คนอื่นๆ กำลังจัดการกับแอปพลิเคชัน SOA ที่กำลังขยายและพัฒนาไปเป็นแอปพลิเคชันไมโครเซอร์วิส อื่น ๆ ยังคงย้ายไปที่แอปพลิเคชันแบบไร้เซิร์ฟเวอร์และบางส่วนกำลังใช้ชุดค่าผสมข้างต้น หลักการที่ระบุไว้ในบทความใช้กับแต่ละระบบเหล่านี้โดยมีการแก้ไขเล็กน้อยเพียงเล็กน้อยเท่านั้น

หลักการ

ตอนนี้เรามีความเข้าใจร่วมกันว่าแอปพลิเคชันสมัยใหม่และสแต็กสมัยใหม่คืออะไร ก็ถึงเวลาเจาะลึกสถาปัตยกรรมและหลักการพัฒนาที่จะให้บริการคุณได้ดีในการพัฒนา ใช้งาน และบำรุงรักษาแอปพลิเคชันสมัยใหม่

หลักการข้อหนึ่งดูเหมือนว่า "สร้างแอปพลิเคชั่นขนาดเล็ก" เรียกมันว่า หลักความเล็ก. มีการใช้งานที่ซับซ้อนอย่างเหลือเชื่อที่ประกอบด้วยชิ้นส่วนที่เคลื่อนไหวจำนวนมาก ในทางกลับกัน การสร้างแอปพลิเคชันจากส่วนประกอบขนาดเล็กที่ไม่ต่อเนื่องช่วยให้ออกแบบ บำรุงรักษา และทำงานกับแอปพลิเคชันโดยรวมได้ง่ายขึ้น (โปรดทราบว่าเราพูดว่า "ทำให้ง่ายขึ้น" ไม่ใช่ "ทำให้เรียบง่าย")

หลักการประการที่สองคือ เราสามารถเพิ่มประสิทธิภาพการทำงานของนักพัฒนาได้โดยการช่วยให้พวกเขามุ่งเน้นไปที่คุณสมบัติที่พวกเขากำลังพัฒนา ในขณะเดียวกันก็ปลดปล่อยพวกเขาจากความกังวลเกี่ยวกับโครงสร้างพื้นฐานและ CI/CD ในระหว่างการนำไปใช้งาน สรุปแล้วแนวทางของเรา มุ่งเน้นไปที่นักพัฒนา.

สุดท้าย ทุกอย่างเกี่ยวกับแอปพลิเคชันของคุณต้องเชื่อมต่อกับเครือข่าย ในช่วง 20 ปีที่ผ่านมา เราได้ก้าวไปสู่อนาคตของเครือข่าย เนื่องจากเครือข่ายเร็วขึ้นและแอปพลิเคชันซับซ้อนมากขึ้น ดังที่เราได้เห็น แอปพลิเคชันสมัยใหม่จะต้องใช้ผ่านเครือข่ายโดยไคลเอ็นต์ที่แตกต่างกันจำนวนมาก การนำการคิดแบบเครือข่ายมาใช้กับสถาปัตยกรรมมีประโยชน์อย่างมากที่เข้ากันได้ดี หลักความเล็ก และแนวคิดแนวทาง นักพัฒนาที่มุ่งเน้น.

หากคุณคำนึงถึงหลักการเหล่านี้เมื่อออกแบบและใช้งานแอปพลิเคชัน คุณจะมีความได้เปรียบอย่างปฏิเสธไม่ได้ในการพัฒนาและส่งมอบผลิตภัณฑ์ของคุณ

ลองดูหลักการทั้งสามนี้โดยละเอียด

หลักความเล็ก

เป็นเรื่องยากสำหรับสมองของมนุษย์ที่จะรับรู้ข้อมูลจำนวนมากในเวลาเดียวกัน ในทางจิตวิทยา คำว่าภาระทางความคิดหมายถึงจำนวนความพยายามทางจิตทั้งหมดที่จำเป็นในการเก็บข้อมูลไว้ในหน่วยความจำ การลดภาระด้านความรู้ความเข้าใจของนักพัฒนาถือเป็นเรื่องสำคัญ เพราะจะช่วยให้พวกเขามุ่งเน้นไปที่การแก้ปัญหาแทนที่จะเก็บรูปแบบที่ซับซ้อนในปัจจุบันของแอปพลิเคชันทั้งหมดและคุณสมบัติที่กำลังพัฒนาอยู่ในหัวของพวกเขา

หลักการพัฒนาแอพพลิเคชั่นสมัยใหม่จาก NGINX ส่วนที่ 1

แอปพลิเคชันสลายตัวด้วยเหตุผลดังต่อไปนี้:

  • ลดภาระทางปัญญาสำหรับนักพัฒนา
  • การเร่งความเร็วและการลดความซับซ้อนของการทดสอบ
  • จัดส่งการเปลี่ยนแปลงอย่างรวดเร็วในแอปพลิเคชัน


มีหลายวิธีในการลดภาระทางความคิดของนักพัฒนา และนี่คือที่มาของหลักการของความเล็ก

ต่อไปนี้เป็นสามวิธีในการลดภาระทางความคิด:

  1. ลดกรอบเวลาที่พวกเขาต้องพิจารณาเมื่อพัฒนาคุณลักษณะใหม่ – ยิ่งกรอบเวลาสั้นลงเท่าใด โหลดความรู้ความเข้าใจก็จะยิ่งน้อยลงเท่านั้น
  2. ลดปริมาณโค้ดที่ต้องดำเนินการเพียงครั้งเดียว - โค้ดน้อยลง - โหลดน้อยลง
  3. ลดความซับซ้อนของขั้นตอนการเปลี่ยนแปลงเพิ่มเติมในแอปพลิเคชัน

ลดกรอบเวลาในการพัฒนา

ลองกลับไปที่วันที่วิธีการ waterfall เป็นมาตรฐานสำหรับกระบวนการพัฒนา และกรอบเวลาตั้งแต่หกเดือนถึงสองปีในการพัฒนาหรืออัปเดตแอปพลิเคชันถือเป็นเรื่องปกติ โดยทั่วไปแล้ว วิศวกรจะอ่านเอกสารที่เกี่ยวข้องก่อน เช่น ข้อกำหนดของผลิตภัณฑ์ (PRD) เอกสารอ้างอิงระบบ (SRD) พิมพ์เขียวสถาปัตยกรรม และเริ่มรวมสิ่งเหล่านี้ทั้งหมดเข้าด้วยกันเป็นแบบจำลองความรู้ความเข้าใจเดียวตามที่พวกเขาเข้ารหัส เมื่อข้อกำหนดและสถาปัตยกรรมเปลี่ยนไป จึงต้องใช้ความพยายามอย่างมากในการแจ้งให้ทั้งทีมทราบเกี่ยวกับการอัปเดตแบบจำลองความรู้ความเข้าใจ วิธีการดังกล่าวที่แย่ที่สุดอาจทำให้งานเป็นอัมพาตได้

การเปลี่ยนแปลงที่ใหญ่ที่สุดในกระบวนการพัฒนาแอปพลิเคชันคือการนำระเบียบวิธีแบบอไจล์มาใช้ หนึ่งในคุณสมบัติหลักของวิธีการ agile เป็นการพัฒนาซ้ำ ในทางกลับกัน สิ่งนี้นำไปสู่การลดภาระทางความคิดของวิศวกร แทนที่จะกำหนดให้ทีมพัฒนาใช้งานแอปพลิเคชันในรอบเดียว agile แนวทางช่วยให้คุณมุ่งเน้นไปที่โค้ดจำนวนเล็กน้อยที่สามารถทดสอบและปรับใช้ได้อย่างรวดเร็ว ในขณะเดียวกันก็ได้รับคำติชมด้วย โหลดความรู้ความเข้าใจของแอปได้เปลี่ยนจากกรอบเวลาหกเดือนเป็นสองปีโดยมีข้อกำหนดจำนวนมากสำหรับการเพิ่มหรือการเปลี่ยนแปลงฟีเจอร์สองสัปดาห์ที่กำหนดเป้าหมายไปที่ความเข้าใจที่เบลอมากขึ้นของแอปขนาดใหญ่

การเปลี่ยนจุดสนใจจากแอปพลิเคชันขนาดใหญ่ไปสู่ฟีเจอร์ขนาดเล็กเฉพาะที่สามารถทำได้ใน sprint สองสัปดาห์ โดยคำนึงถึงฟีเจอร์ไม่เกินหนึ่งฟีเจอร์ก่อนหน้า sprint ถัดไป ถือเป็นการเปลี่ยนแปลงที่สำคัญ สิ่งนี้ทำให้เราสามารถเพิ่มผลผลิตในการพัฒนาในขณะที่ลดภาระทางความคิดซึ่งมีความผันผวนอยู่ตลอดเวลา

ในวิธีการ agile แอปพลิเคชันขั้นสุดท้ายคาดว่าจะเป็นเวอร์ชันดัดแปลงเล็กน้อยจากแนวคิดดั้งเดิม ดังนั้นจุดสิ้นสุดของการพัฒนาจึงจำเป็นต้องคลุมเครือ เฉพาะผลลัพธ์ของการวิ่งแต่ละครั้งเท่านั้นที่สามารถชัดเจนและแม่นยำได้

ฐานรหัสขนาดเล็ก

ขั้นตอนต่อไปในการลดภาระการรับรู้คือการลด codebase ตามกฎแล้ว แอปพลิเคชันสมัยใหม่มีขนาดใหญ่มาก แอปพลิเคชันระดับองค์กรที่แข็งแกร่งสามารถประกอบด้วยไฟล์นับพันไฟล์และโค้ดหลายแสนบรรทัด ขึ้นอยู่กับวิธีการจัดระเบียบไฟล์ การเชื่อมโยงและการขึ้นต่อกันระหว่างโค้ดและไฟล์อาจชัดเจน หรือในทางกลับกัน แม้แต่การดีบักโค้ดที่ใช้เองก็อาจเป็นปัญหาได้ ขึ้นอยู่กับไลบรารีที่ใช้และเครื่องมือดีบั๊กแยกความแตกต่างระหว่างไลบรารี/แพ็คเกจ/โมดูลและโค้ดแบบกำหนดเองได้ดีเพียงใด

การสร้างแบบจำลองทางความคิดในการทำงานของโค้ดของแอปพลิเคชันอาจใช้เวลานานพอสมควร และสร้างภาระทางความคิดครั้งใหญ่ให้กับนักพัฒนาอีกครั้ง นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งสำหรับฐานรหัสเสาหิน ซึ่งมีรหัสจำนวนมาก ปฏิสัมพันธ์ระหว่างองค์ประกอบการทำงานที่ไม่ได้กำหนดไว้อย่างชัดเจน และการแยกวัตถุที่สนใจมักไม่ชัดเจนเนื่องจากไม่เคารพขอบเขตการทำงาน

หนึ่งในวิธีที่มีประสิทธิภาพในการลดภาระทางความคิดของวิศวกรคือการเปลี่ยนไปใช้สถาปัตยกรรมไมโครเซอร์วิส ในแนวทางของไมโครเซอร์วิส แต่ละบริการจะเน้นที่ชุดคุณลักษณะหนึ่งชุด ในขณะที่ความหมายของบริการมักจะถูกกำหนดและเข้าใจได้ ขอบเขตของบริการก็ชัดเจนเช่นกัน โปรดจำไว้ว่าการสื่อสารกับบริการนั้นทำผ่าน API ดังนั้นข้อมูลที่สร้างโดยบริการหนึ่งสามารถส่งผ่านไปยังอีกบริการหนึ่งได้อย่างง่ายดาย

การโต้ตอบกับบริการอื่นๆ มักจะจำกัดเฉพาะบริการผู้ใช้เพียงไม่กี่รายและบริการของผู้ให้บริการบางรายที่ใช้การเรียก API ที่เรียบง่ายและสะอาด เช่น การใช้ REST ซึ่งหมายความว่าภาระทางปัญญาของวิศวกรจะลดลงอย่างมาก ความท้าทายที่ยิ่งใหญ่ที่สุดยังคงเป็นการทำความเข้าใจรูปแบบการโต้ตอบของบริการและวิธีต่างๆ เช่น การทำธุรกรรมที่เกิดขึ้นในบริการต่างๆ ผลที่ตามมาคือ การใช้ไมโครเซอร์วิสช่วยลดภาระการรับรู้โดยการลดจำนวนโค้ด กำหนดขอบเขตบริการที่ชัดเจน และให้ความเข้าใจในความสัมพันธ์ระหว่างผู้ใช้และผู้ให้บริการ

การเปลี่ยนแปลงทีละน้อย

องค์ประกอบสุดท้ายของหลักการ ความเล็ก คือการบริหารการเปลี่ยนแปลง เป็นสิ่งล่อใจสำหรับนักพัฒนาโดยเฉพาะที่จะมองไปที่ฐานรหัส (แม้แต่รหัสเก่าของพวกเขาเอง) และพูดว่า "นี่มันไร้สาระ เราจำเป็นต้องเขียนใหม่ทั้งหมด" บางครั้งนี่เป็นการตัดสินใจที่ถูกต้อง และบางครั้งก็ไม่ใช่ มันทำให้ภาระของการเปลี่ยนแปลงแบบจำลองทั่วโลกอยู่ในทีมพัฒนา ซึ่งจะนำไปสู่ภาระทางความคิดจำนวนมหาศาล เป็นการดีกว่าสำหรับวิศวกรที่จะมุ่งเน้นไปที่การเปลี่ยนแปลงที่พวกเขาสามารถทำได้ระหว่างการวิ่ง เพื่อให้พวกเขาสามารถเปิดตัวฟังก์ชันที่จำเป็นได้อย่างทันท่วงที แม้ว่าจะค่อยเป็นค่อยไปก็ตาม ผลิตภัณฑ์ขั้นสุดท้ายควรมีลักษณะคล้ายกับผลิตภัณฑ์ที่วางแผนไว้ล่วงหน้า แต่มีการดัดแปลงและทดสอบบางอย่างเพื่อให้เหมาะกับความต้องการของลูกค้า

เมื่อเขียนโค้ดส่วนใหญ่ใหม่ บางครั้งไม่สามารถส่งการเปลี่ยนแปลงได้อย่างรวดเร็ว เนื่องจากการพึ่งพาระบบอื่นเข้ามาเกี่ยวข้อง เพื่อควบคุมการไหลของการเปลี่ยนแปลง คุณสามารถใช้การซ่อนคุณลักษณะได้ โดยหลักการแล้ว หมายความว่าฟังก์ชันนี้อยู่ระหว่างการใช้งานจริง แต่ไม่สามารถใช้งานได้โดยใช้การตั้งค่าตัวแปรสภาพแวดล้อม (env-var) หรือกลไกการกำหนดค่าอื่นๆ หากรหัสได้ผ่านกระบวนการควบคุมคุณภาพทั้งหมดแล้ว รหัสนั้นอาจจบลงที่การผลิตในสถานะแฝง อย่างไรก็ตาม กลยุทธ์นี้จะใช้ได้เฉพาะเมื่อเปิดใช้งานฟีเจอร์นี้ในท้ายที่สุดเท่านั้น มิฉะนั้นจะทำให้โค้ดยุ่งเหยิงและเพิ่มภาระทางปัญญาที่นักพัฒนาจะต้องจัดการเพื่อให้มีประสิทธิผล การจัดการการเปลี่ยนแปลงและการเปลี่ยนแปลงส่วนเพิ่มช่วยให้โหลดการรับรู้ของนักพัฒนาอยู่ในระดับที่เหมาะสม

วิศวกรต้องเอาชนะความยากลำบากมากมายแม้จะมีการแนะนำฟังก์ชันเพิ่มเติมอย่างง่ายๆ ในส่วนของการจัดการ การลดภาระที่ไม่จำเป็นในทีมควรเป็นไปอย่างรอบคอบ เพื่อให้สามารถโฟกัสไปที่องค์ประกอบการทำงานหลักได้ มีสามสิ่งที่คุณสามารถทำได้เพื่อช่วยทีมพัฒนาของคุณ:

  1. ใช้วิธีการ agileเพื่อจำกัดกรอบเวลาที่ทีมต้องมุ่งเน้นไปที่คุณสมบัติหลัก
  2. ใช้แอปพลิเคชันของคุณเป็นไมโครเซอร์วิสหลายตัว สิ่งนี้จะจำกัดจำนวนของคุณสมบัติที่สามารถนำไปใช้ได้และเสริมสร้างขอบเขตที่ทำให้ภาระทางปัญญาทำงาน
  3. ชอบการเปลี่ยนแปลงทีละน้อยมากกว่าการเปลี่ยนแปลงขนาดใหญ่และเทอะทะ เปลี่ยนโค้ดชิ้นเล็กๆ ใช้การซ่อนคุณลักษณะเพื่อใช้การเปลี่ยนแปลงแม้ว่าจะไม่สามารถมองเห็นได้ทันทีหลังจากเพิ่มเข้าไป

หากคุณใช้หลักการของความเล็กในการทำงาน ทีมของคุณจะมีความสุขมากขึ้น มีสมาธิมากขึ้นกับการนำคุณลักษณะที่จำเป็นไปใช้ และมีแนวโน้มที่จะเปิดตัวการเปลี่ยนแปลงเชิงคุณภาพเร็วขึ้น แต่นี่ไม่ได้หมายความว่างานจะไม่ซับซ้อนขึ้น บางครั้ง ในทางกลับกัน การแนะนำฟังก์ชันใหม่จำเป็นต้องปรับเปลี่ยนบริการหลายอย่าง และกระบวนการนี้อาจยากกว่าสถาปัตยกรรมแบบเสาหินที่คล้ายกัน ไม่ว่าในกรณีใด ประโยชน์ของการใช้วิธีขนาดเล็กก็คุ้มค่า

จบภาคแรก.

ในไม่ช้าเราจะเผยแพร่ส่วนที่สองของการแปล และตอนนี้เรากำลังรอความคิดเห็นของคุณและขอเชิญคุณ วันเปิดเทอมซึ่งจะมีขึ้นในวันนี้ เวลา 20.00 น.

ที่มา: will.com

เพิ่มความคิดเห็น