รู้จักกับ Software architecture แบบง่ายๆ จาก Architecture for Everyone
หากเราพูดถึง Software architecture เมื่อไรที่เรายกประเด็นเหล่านี้ขึ้นมาคุยในวงคน IT และ non-IT มันมักจะเป็นเรื่องไกลตัว หนำซ้ำเรามักจะเจอ ศัพท์เทคนิค หรือ jargon เต็มไปหมด แปลกนะทำไมเรื่องบางเรื่องถึงใช้การเปรียบเทียบอุปมาอุปมัยเข้าใจกันง่ายๆ แต่พอเป็นเรื่องนี้กลับใช้คำอะไรก็ไม่รู้เต็มไปหมด ทั้งๆ ที่มันเป็นเรื่องที่สำคัญมากๆ
non-IT: “แม่งพูดไรวะ คุยไม่รู้เรื่องเลย ศัพท์อะไรก็ไม่รู้”
IT: “มันยากตรงไหน แค่นี้ก็ไม่รู้เรื่อง” หรือไม่ก็ “ก็ไม่รู้จะหาคำไหนมาเปรียบเทียบอ่ะ”
ในเมื่อ Software architecture มันไม่ใช่เรื่องไกลตัวระหว่างทุกคนเลย ดังนั้นเรามาเข้าใจกันแบบฉบับใส่น้ำร้อนกินได้เลยดีกว่า
จากบทความ Architecture for Everyone โดยคุณ David Whitney มีศัพท์เทคนิคที่น่าสนใจหลายอัน ดังนี้
หมวด Web server
- Web: ย่อมาจาก World wide web เป็น computer หลายๆ เครื่องที่เชื่อมต่อกันผ่าน internet
- Web page: สิ่งที่เอาไปแสดงผลใน web browser
- HTTP protocol: กลุ่มคำสั่งที่ส่งไปหา computer อีกเครื่องหนึ่งเพื่อบอกว่า “เฮ้ย ส่งข้อความมาหน่อย” (web browser ก็ทำงานแบบนี้นิหว่า)
- Web server: program ที่ทำงานอยู่บน computer ที่เชื่อมต่อกับ internet เพื่อรอรับ “ข้อความ” และตอบ “ข้อความ” กลับไป โดยคาดหวังไว้ว่า คนที่ส่งก็สามารถที่จะรับ response ได้ด้วย (ข้อความก็คือ HTTP request และ response นั่นเอง)
- DNS: สิ่งที่เปลี่ยนจาก
raksit31667.github.io
เป็น IP address185.199.110.153
- REST: ย่อมาจาก Representational State Transfer เป็นรูปแบบของ web ที่ส่ง “ข้อความ” ที่เป็นตัวแทน (representation) ของสถานะที่เก็บไว้บน computer สักเครื่อง (state) ซึ่ง representation ก็อาจจะเปลี่ยนไปเรื่อยตาม state อาจจะมาในรูปของ web page, document หรือรูปภาพก็ได้
- RESTful: web server ที่ใช้ architecture แบบ REST (แสดงว่า web มันก็เป็น RESTful อยู่แล้วนี่หว่า)
หมวด Web application patterns
- MVC: ย่อมาจาก Model-View-Controller เป็น design ที่แยกการทำงานของ application (controller) และข้อมูล (model) และการแสดงผล (view) ออกจากกัน ตัวอย่างเช่น เวลา user เข้า
http://raksit31667.github.io/about
application ก็จะไปเรียกคำสั่ง (controller) ที่ชื่อว่าAbout
ซึ่งจะได้ผลลัพธ์เป็นข้อมูล (model) ซึ่งจะถูกนำไปแสดงผลผ่าน web page (view) เป็นต้น - SPA: ย่อมาจาก Single Page Application เป็น design ที่ย้าย application ที่ใช้ MVC pattern จากการทำงานใน server ไปไว้ที่ client แทน เช่น web browser ของผู้ใช้งาน ซึ่งเราจะรู้จัก pattern ผ่าน Angular หรือ React หรือ Vue ซึ่งจะมีรายละเอียดยิบย่อยต่างกันไป
- Static sites: เป็น application ที่จะทำการสร้างหน้า web ให้ผ่าน code ที่เราเขียน และเอาไปไว้บน web server สักที่โดยที่ไม่ต้องไปจัดการหลังบ้านในฝั่ง server มากมาย
https://devrant.com/rants/1517545/found-on-the-web-true-af
แล้วเราควรจะใช้อันไหนดีล่ะ
ขึ้นอยู่กับสถานการณ์ครับ เช่น ถ้าระบบของเรามี UX/UI แบบอลังการงานสร้าง เราก็ไปใช้ SPA ดีกว่า แต่ก็ต้องมาเรียนรู้การพัฒนาที่ซับซ้อน หรือถ้าเราคาดว่าระบบของเรามีการเก็บข้อมูล แต่ก็ไม่ได้มี UX/UI ซับซ้อนเราก็ไปทาง MVC ที่ run บน server ก็ได้ครับ ส่วน static sites ก็จะเหมาะกับพวก blog หรือ marketing website ขำๆ เพราะมันถูกสุดและ scale ได้เร็ว
หมวด Design patterns
- MVC: แยกการทำงานของ application (controller) และข้อมูล (model) และการแสดงผล (view) ออกจากกัน จะได้ไม่งง
- ORM: ย่อมาจาก Object-relational mapping วิธีการแปลงข้อมูลระหว่าง สิ่งที่ระบุไว้ในรูปแบบ Object กับ และฐานข้อมูลที่เก็บข้อมูลแบบมีความสัมพันธ์กัน (relational database)
- Active record: เป็นแนวคิดที่ต่อจาก ORM คือ Object ควรจะ save หรือ update ตัวเองได้ โดยไม่ได้สนใจว่ามันจะไปผูกกับ database อะไรยังไง
- Repository: แยกการทำงานของ application ออกจากการเข้าถึงแหล่งข้อมูล (data source) โดยใช้ตัวกลางมาเชื่อมเอา
- Dependecy injection: ให้คนที่เรียกใช้ class หรือ function นั้นจัดการกับ dependency ของมันเอง
- Factory: เอา code ที่ใช้ในการสร้าง Object หนึ่ง ไปไว้ในที่ๆ เดียว
- Adapter: เชื่อมสิ่ง 2 สิ่งที่ปกติไม่ได้ทำงานด้วยกัน เข้าด้วยกัน
- Strategy: กำหนดหลายๆ วิธีการในการทำบางอย่าง ซึ่งสามารถสลับเข้า-ออกได้
“Design patterns are just 1990s versions of an accepted and popular StackOverflow answers.” - David Whitney
หมวด Distributed systems
- Circuit breaker: เมื่อระบบเริ่มเกิดความผิดพลาดขึ้น ระบบจะทำการตัดวงจรด้วยการนำ request มารอต่อคิวแทนที่จะส่งต่อเพราะรู้ว่าส่งไปยังไงก็ฟัง สักพักนึงลองปล่อย request เข้าไปในระบบสักอันนึงเพื่อดูว่าระบบมันกลับมาใช้งานได้หรือยัง ถ้าสำเร็จก็จะกลับมาเปิดระบบใหม่อีกครั้ง มันคือการป้องกันไม่ให้ระบบส่วนอื่นๆ ทำงานผิดพลาดตามไปด้วย
- Idempotency: การออกแบบระบบที่สามารถ submit ข้อมูลได้โดยไม่เบิ้ล เช่น ถ้า request ถอนเงินออกจากบัญชี 50 บาท การส่ง request นี้ซ้ำๆ สุดท้ายแล้วเงินในบัญชีเราจะลดไปแค่ 50 บาท
- Retry: เมื่อระบบมีการทำงานผิดพลาด ระบบสามารถจัดการ request ซ้ำได้อีกจำกัดครั้ง แน่นอนว่าการทำงานจะต้องเป็น Idempotency ด้วย
- Bulkhead: การจำกัดความสามารถในการรับ request เช่น hardware, ขนาดของ network และจำนวน request เพื่อป้องกันไม่ให้ระบบส่วนอื่นๆ ต้องรับ load มากเกินไป วิธีการทำก็อย่างเช่น กำหนดจำนวน request สูงสุดที่สามารถรับได้ในระยะเวลาหนึ่ง, การกำหนด timeout, รวมไปถึงการเผื่อ resource ไว้ทำงานใหญ่ๆ เช่น payment เป็นต้น
หมวด Microservices
เริ่มจากเดิมที่เราสร้างระบบขึ้นมาเป็นก้อนๆ เดียว หรือ Monolith ซึ่งข้อดีคือง่ายเพราะดูแลแค่ก้อนเดียว แต่พอระบบมีความซับซ้อนขึ้นทั้งด้าน business และ technical ทำให้ค่าใช้จ่ายในการดูแลก้อนนี้มันสูงขึ้นมากๆ แถมเวลมีส่วนใดส่วนหนึ่งมีปัญหา ระบบก็ล่มหมดตามไปด้วย จึงมีแนวคิดในการแบ่งส่วนของการทำงานออกจากกันเพื่อลดความเสี่ยงในการนำระบบขึ้น แต่ละส่วนดูแลง่ายขึ้น เรียกว่า 3-tier และ N-tier
ถึงแม้จะเราจะแบ่งการทำงานออกเป็น presentation-business-data tier แล้ว ปัญหาที่ตามมาคือเมื่อมีการเปลี่ยนแปลงใหญ่ๆ จะมีความซับซ้อนมากขึ้นเพราะต้องแก้ไขทุกๆ tier จึงเกิดแนวคิดในการมองทุก tier เป็นส่วนเดียวกัน เกิดเป็น Service-oriented architecture (SOA)
By Benjamin F Clay - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=27808423
SOA ประกอบไปด้วย 3 ส่วนหลักๆ คือ Provider (service ต่างๆ) และ Consumer (เช่น user และ service ที่เรียกใช้งาน) ซึ่ง 2 ส่วนนี้จะคุยกันผ่านทาง Enterprise service bus (ESB) ซึ่งปัญหาของ ESB คือมันเป็นจุดเดียวหลักๆ ที่่มีโอกาสที่จะทำให้ทั้งระบบล่ม (single point of failure) และเมื่อแต่ละ service มีการใช้งานร่วมกัน ก็จะผูกมัดกันอย่างมากด้วย
Microservices จึงถือกำเนิดขึ้นโดยจะต้องมีเงื่อนไขต่างๆ ดังนี้ ไม่เช่นนั้นจะถือว่าเป็น Distributed Monolith ซึ่งบอกได้เลยว่าเจ็บปวดกว่าเดิมเยอะ
- เน้นการแบ่ง service ตามการทำงาน ไม่ใช่ data
- แยกส่วนจัดเก็บข้อมูลออกจากกัน แต่อาจจะ share กันได้บ้าง
- สื่อสารกันผ่านตัวกลางข้างนอก service ที่กำหนดร่วมกัน
- เมื่อมีการเปลี่ยนแปลงแล้วกระทบ service หลายๆ อันพร้อมกัน ถือว่า service เหล่านั้นเป็น service เดียวกัน
- สามารถปิดหรือล่มได้ โดยไม่ส่งผลกระทบต่อ service อื่นๆ
https://mobile.twitter.com/kerfors/status/581530381180096512
สิ่งที่แลกมากับ Microservices คือความซับซ้อนในการออกแบบและดูแลรักษา
หมวดอื่นๆ
- API: วิธีการเรียกใช้ program
- Load balancer: program ที่รับ request และเลือกส่งต่อให้ server ที่ไม่ได้ยุ่งกับการจัดการ request อื่นๆ อยู่ ออกแบบมาเพื่อให้ระบบรับ request ได้เยอะๆ ซึ่งก็ต้องแลกมากับการเพิ่มจุดที่มีโอกาสที่ระบบจะล่ม (point of failure) เพราะถ้า load balancer ตาย server ก็ไม่ได้รับ request นั่นเอง
- Distributed caching: การเก็บ “ข้อมูลที่เป็นผลลัพธ์จากการคำนวณหนักๆ ใช้ระยะเวลาเยอะๆ” บน memory ของ computer หลายๆ เครื่อง เวลาเราเพิ่มข้อมูลลงไป ระบบจะสร้าง key ที่ระบุเครื่องที่เก็บข้อมูลไว้ด้วย แล้วแจกจ่ายให้ทุกเครื่องที่อยู่กลุ่มเดียวกัน ดังนั้นถ้าเราไปค้นหาข้อมูลจากเครื่องไหนก็ตาม เราก็จะได้ผลลัพธ์เดียวกันเพราะระบบรู้ว่าจะต้องไปหาข้อมูลนี้จากเครื่องไหน สิ่งที่เราจะได้เลยคือความเร็วและระบบยังทำงานต่อได้ถ้ามีบางเครื่องล่ม แต่ก็ต้องแลกกับ delay นิดหน่อยตอนที่แจกจ่าย key
- Content delivery networks (CDNs): web server ที่ run โดยคนอื่นทั่วโลก โดยจะเปิดให้เรา upload ข้อมูลขึ้นไป แล้วระบบจะทำการ copy ข้อมูลกระจายไปอยู่ทุก server ทั่วโลก แล้วเวลาที่มี user อยากเรียกดูข้อมูล DNS มันก็จะชี้ไปหา server ที่อยู่ใกล้กับ user มากที่สุด เหมาะสำหรับการเก็บพวกรูป video หรือ file ใหญ่ๆ สิ่งที่เราได้คือลดการทำงานของ server ของเรา ซึ่งก็ต้องแลกมากับการเพิ่มจุดที่มีโอกาสที่ระบบจะล่ม
- Backend-for-frontend (BFF): API ที่สร้างมาเพื่อ application หนึ่งอันโดยเฉพาะ เพื่อลด concern ในการจัดการสิ่งที่แตกต่างกันของ application ทั้ง hardware, performance และขนาดของ network มีข้อดีคือ แบ่งการทำงานออกเป็นส่วนๆ เพื่อความสามารถใหม่ๆ เข้าไปได้ง่าย ลดผลกระทบจากการเปลี่ยนแปลง แต่การสร้าง layer มาก็ต้องเจอกับปัญหาใหม่ (n+1) ดังนั้น BFF ก็ยังคงต้องมีสิ่งที่ระบบหลักต้องมีนะ
- Hexagonal architecture: การออกแบบที่ใช้ port-and-adapter pattern โดยที่แต่ละส่วนการทำงานมีอิสระที่จะใช้ framework, dependency, data storage, UI อะไรก็ได้ และจะเชื่อมกันตาม adapter ข้างนอก ซึ่งจะถอดเข้า-ออกตอนไหนก็ได้ มีแนวคิดที่คล้ายๆ กันชื่อว่า The Twelve-Factor App
จะเห็นว่าเรื่อง Architecture มันเป็นเรื่องของทุกคนจริงๆ เนื่องจากมันมีข้อดี-ข้อเสียที่ต้องจ่าย ดังนั้นทุกคนควรจะได้ประโยชน์จากมันจริงๆ และ Architecture ที่ดีไม่ใช่อันที่ซับซ้อนที่สุด แต่เป็นอันที่เรียบง่ายเมื่อเทียบกับความต้องการในปัจจุบัน และสามารถปรับแก้เมื่อมีการเปลี่ยนแปลงได้ในอนาคต