สถาปัตยกรรมซอฟต์แวร์และการออกแบบระบบ: ภาพใหญ่และคู่มือทรัพยากร

สวัสดีเพื่อนร่วมงาน

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

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

สถาปัตยกรรมซอฟต์แวร์และการออกแบบระบบ: ภาพใหญ่และคู่มือทรัพยากร

ภาพรวม ไอแซค สมิธ จากอันสแปลช

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

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

  • ปัญหาที่เราพยายามแก้ไขคืออะไร?
  • จำนวนผู้ใช้สูงสุดที่จะโต้ตอบกับระบบของเราคือเท่าใด
  • เราจะใช้รูปแบบการเขียนและการอ่านข้อมูลแบบใด
  • กรณีความล้มเหลวที่คาดหวังคืออะไร เราจะจัดการอย่างไร
  • ความคาดหวังสำหรับความสอดคล้องและความพร้อมใช้งานของระบบคืออะไร?
  • คุณต้องคำนึงถึงข้อกำหนดใด ๆ ที่เกี่ยวข้องกับการตรวจสอบและกฎระเบียบภายนอกเมื่อทำงานหรือไม่?
  • เราจะจัดเก็บข้อมูลที่ละเอียดอ่อนประเภทใด

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

ตั้งค่าระดับเริ่มต้น

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

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

เอาล่ะ จะเริ่มตรงไหนดี? ยู ดอนน่า มาร์ติน่า มีพื้นที่เก็บข้อมูลบน GitHub ที่เรียกว่า การออกแบบระบบไพรเมอร์โดยคุณจะได้เรียนรู้วิธีการออกแบบระบบขนาดใหญ่พร้อมทั้งเตรียมตัวสัมภาษณ์ในหัวข้อนี้ พื้นที่เก็บข้อมูลมีส่วนพร้อมตัวอย่าง สถาปัตยกรรมที่แท้จริงโดยเฉพาะอย่างยิ่งจะพิจารณาว่าพวกเขาเข้าใกล้การออกแบบระบบของตนอย่างไร บริษัทที่มีชื่อเสียงบางแห่งเช่น Twitter, Uber เป็นต้น

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

สร้างองค์ความรู้เกี่ยวกับการจัดเก็บและเรียกค้นข้อมูล

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

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

สถาปัตยกรรมซอฟต์แวร์และการออกแบบระบบ: ภาพใหญ่และคู่มือทรัพยากร

ภาพรวม ซามูเอล เซลเลอร์ จากอันสแปลช

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

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

รูปแบบการสื่อสาร

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

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

สถาปัตยกรรมซอฟต์แวร์และการออกแบบระบบ: ภาพใหญ่และคู่มือทรัพยากร

ภาพรวม โทนี่ สต๊อดดาร์ด จากอันสแปลช

เมื่อจัดระเบียบการสื่อสารกับโลกภายนอกเป็นสิ่งสำคัญมากเสมอ ความปลอดภัยซึ่งบทบัญญัติดังกล่าวจำเป็นต้องได้รับการดำเนินการอย่างจริงจังและกระตือรือร้นด้วย

การกระจายการเชื่อมต่อ

ฉันไม่แน่ใจว่าการแยกหัวข้อนี้ออกเป็นส่วนๆ จะดูสมเหตุสมผลสำหรับทุกคน อย่างไรก็ตาม ฉันจะนำเสนอแนวคิดนี้โดยละเอียดที่นี่ และฉันเชื่อว่าเนื้อหาในส่วนนี้ได้รับการอธิบายอย่างถูกต้องที่สุดโดยใช้คำว่า "การกระจายการเชื่อมต่อ"

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

การกระจายนี้ขึ้นอยู่กับที่รู้จักกันดี ระบบชื่อโดเมน (ดีเอ็นเอส) ระบบดังกล่าวอนุญาตให้มีการแปลงชื่อโดเมน เช่น Weighted Round Robin และวิธีที่ใช้เวลาในการตอบสนองเพื่อช่วยกระจายภาระงาน

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

คุณควรรู้เกี่ยวกับ เครือข่ายการจัดส่งเนื้อหา (ซีดีเอ็น). CDN คือเครือข่ายพร็อกซีเซิร์ฟเวอร์แบบกระจายทั่วโลกที่ส่งข้อมูลจากโหนดที่ตั้งทางภูมิศาสตร์ใกล้กับผู้ใช้รายใดรายหนึ่ง ควรใช้ CDN หากคุณทำงานกับไฟล์คงที่ที่เขียนด้วย JavaScript, CSS และ HTML นอกจากนี้ บริการคลาวด์ที่ให้บริการจัดการการรับส่งข้อมูลเป็นเรื่องปกติในปัจจุบัน เช่น ผู้จัดการจราจร Azureช่วยให้คุณกระจายไปทั่วโลกและลดเวลาแฝงเมื่อทำงานกับเนื้อหาแบบไดนามิก อย่างไรก็ตาม บริการดังกล่าวมักจะมีประโยชน์ในกรณีที่คุณต้องทำงานกับบริการเว็บไร้สัญชาติ

เรามาพูดถึงตรรกะทางธุรกิจกันดีกว่า การจัดโครงสร้างตรรกะทางธุรกิจ โฟลว์งาน และส่วนประกอบ

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

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

แนวทางการทำงานร่วมกัน

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

สถาปัตยกรรมซอฟต์แวร์และการออกแบบระบบ: ภาพใหญ่และคู่มือทรัพยากร

ภาพรวม คาไลดิโก้ จากอันสแปลช

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

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

ที่มา: will.com

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