Weekly MicroClosure #1: Monolith Hell

Trong thế giới công nghệ hiện đại, Microservices đang trở thành một chủ đề nổi bật, mở ra những cánh cửa mới cho kiến trúc phần mềm linh hoạt và dễ bảo trì. Mỗi tuần, chúng tôi sẽ đưa đến cho bạn những kiến thức chất lượng và sâu sắc thông qua một quyển sách tuyệt vời là "Microservices Patterns: With Examples in Java "của tác giả Chris Richardson, giúp bạn hiểu rõ hơn về ưu điểm, thách thức và cách triển khai chúng một cách hiệu quả.

"Weekly MicroClosure" không chỉ là nơi chia sẻ thông tin kỹ thuật, mà còn là nơi để bạn tìm hiểu về những xu hướng mới, các công nghệ liên quan và những lời khuyên thực tế trong lĩnh vực này.

 

Trong bài viết mở đầu, chúng ta hãy cùng tìm hiểu các ưu điểm và hạn chế của cá mô hình kiến trúc phần mềm truyền thống monolith (kiến trúc nguyên khối) thông qua một ví dụ về công ty tên là FoodToGo (FTGO) nhé.

Ngữ cảnh:

Một công ty giả tưởng là công ty FoodToGo (FTGO) (Hình 1) cung cấp dịch vụ đặt hàng thức ăn trực tuyến (tương tự như GrabFood, BeFood,...). Người dùng sử dụng trang web hoặc ứng dụng di động của FTGO để đặt món ăn từ các nhà hàng địa phương. Trách nhiệm của FTGO bao gồm: 

  • Phối hợp mạng lưới các người vận chuyển (shipper) để giao đồ ăn.
  • Chịu trách nhiệm là cổng thanh toán cho người dùng và nhà hàng. 
  • Các nhà hàng sử dụng trang web FTGO để chỉnh sửa thực đơn và quản lý đơn đặt hàng.
  • Tích hợp nhiều dịch vụ của bên thứ ba: bao gồm Stripe cho cổng thanh toán, Twilio cho tin nhắn và Amazon Simple Email Service (SES) để gửi email.

Hình 1. Công ty FoodToGo (FTGO) (Bing Image Creator, prompt: logo of a company name FoodToGo (FTGO), food deliveries service company)

 

Về mặt kiến trúc, FTGO sử dụng kiến trúc nguyên khối (monolith service): đóng gói tất cả thành phần thành một tệp tin Java WAR (Web Application Resource) duy nhất. Trải qua nhiều năm phát triển hệ thống, kiến trúc của FTGO gia tăng về cả kích thước lẫn độ phức tạp. Điều này dẫn đến nhiều hệ lụy khiến quá trình phát triển phần mềm tại FTGO ngày càng chậm đi. Để làm vấn đề tệ hơn, hệ thống FTGO được phát triển bằng một framework đã lỗi thời. Có thể nói, FTGO có tất cả các triệu chứng của một “Monolith Hell” (Hình 2).

Hình 2. Monolith Hell (Bing Image Creator, prompt:  monolith hell, software development)

1. Kiến trúc ban đầu của FTGO

Hệ thống từ khi sáng lập FTGO có kiến trúc hình lục giác (Hình 3). Nó bao gồm các module về nghiệp vụ được bao quanh bởi các Adapter kết nối với các thành phần còn lại. Các module nghiệp vụ tập trung thực hiện một số nhiệm vụ nhất định cho hệ thống. Ví dụ về có các module về Quản lý đơn hàng, Quản lý giao hàng, Lập hóa đơn và Thanh toán.

Hình 3. Kiến trúc ban đầu được FTGO sử dụng là một kiến trúc Monolith.

Các Adapter dùng các interface để giao tiếp với các phần khác nhau của hệ thống. 

  • Một số Adapter xử lý nội bộ đáp ứng các yêu cầu từ API REST và giao diện Web bằng cách gọi module nghiệp vụ. 
  • Một số Adapter khác giao tiếp với các thành phần bên ngoài: cho phép module nghiệp vụ truy cập vào cơ sở dữ liệu MySQL hay gọi các dịch vụ đám mây như Twilio và Stripe.

2. Ưu điểm của kiến trúc Monolith

  • Phát triển đơn giản: Các nhà phát triển (Dev) tại FTGO sử dụng đồng nhất các công cụ và IDE tương tự nhau tập trung vào việc xây dựng một ứng dụng duy nhất.
  • Dễ dàng thực hiện các thay đổi căn bản đối với ứng dụng: Với kiến trúc ban đầu, FTGO có thể dễ dàng đưa ra những thay đổi căn bản về mã nguồn và cấu trúc của cơ sở dữ liệu.
  • Kiểm tra đơn giản: Các Dev/ Tester đã viết các bài kiểm tra đầu cuối để khởi chạy ứng dụng, gọi API REST và kiểm tra giao diện người dùng với Selenium.
  • Triển khai dễ dàng: Tất cả những gì Dev phải làm là sao chép tệp WAR vào các máy chủ đã cài đặt Tomcat.
  • Dễ dàng mở rộng (Scaling): FTGO thực hiện Scaling hệ thống bằng cách chạy nhiều instance của hệ thống, chúng đứng sau một bộ load balancer.

 

3. Các vấn đề tạo nên một Monolith Hell

Mỗi một sprint, nhóm phát triển FTGO lại thực hiện thêm một số user story, từ đó khiến codebase trở nên lớn hơn theo thời gian. Từ đội Dev ban đầu đã tăng số lượng, phát triển thành nhiều Scrum team, trong đó mỗi team chịu trách nhiệm cho một nhóm chức năng nhất định. (Hình 4)

Hình 4. Kiến trúc FTGO sau một thời gian dài phát triển

 

Sự phức tạp khiến các Dev khóc thét (COMPLEXITY INTIMIDATES DEVELOPERS)​: Như đã trình bày, source code của hệ thống FTGO đã phình to, đến một mức độ mà thậm chí khó có Dev nào có thể thông hiểu toàn bộ. Kết quả là, việc fix bug và xây dựng tính năng mới trở nên khổ sở và tốn thời gian khieesn tình trạng trễ deadline thường xuyên xảy ra. Source code quá phức tạp thì mỗi thay đổi đều có thể dẫn đến một tầng mới của địa ngục. Mỗi thay đổi trên source code (dù thành công) cũng khiến source code trở nên phức tạp hơn.


Tốc độ phát triển quá chậm (DEVELOPMENT IS SLOW): Code base quá lớn khiến ngay cả các công cụ của Dev bị chậm và quá tải. Mỗi lần bạn code xong một vài hàm và muốn kiểm tra chúng: bản build được thực hiện siêu lâu và bạn không thể làm gì. Mỗi lần build, chạy phần mềm, kiểm thử tốn rất nhiều thời gian. Vòng đời edit-build-run-test khiến quá trình phát triển tốn thêm rất nhiều thời gian chờ đợi.

 

Hành trình từ khi commit đến lúc được triển khai đầy gian truân (PATH FROM COMMIT TO DEPLOYMENT IS LONG AND ARDUOUS): Đối với hệ thống này, triển khai các thay đổi vào giai đoạn production là một quá trình lâu dài và khó khăn. (Lưu ý rằng FTGO đã áp dụng CI/CD pipeline lên source code). Vậy, vấn đề đầu tiên là rất nhiều Dev cùng commit vào một code base khiến chúng tạo ra các bản build không ổn định. Các dev của FTGO cố gắng giải quyết vấn đề này bằng cách sử dụng các feature branch, và đối mặt với họ là những lần merge lê thê và trầm cảm.

Một lý do khác khiến việc đưa các thay đổi vào sản xuất mất quá nhiều thời gian là do quá trình testing mất nhiều thời gian. Do codebase rất phức tạp và các thay đổi không được hiểu rõ nên CI pipeline phải thực thi toàn bộ các test case. 

Một số phần của hệ thống thậm chí còn yêu cầu kiểm tra thủ công: đôi khi, thời gian này mất đến hằng ngày để quay trở lại khắc phục nguyên nhân. Kết quả là phải mất vài ngày để hoàn thành một pipeline triển khai version mới.

 

Mở rộng cũng được đấy, nhưng chưa ổn (SCALING IS DIFFICULT): Ví dụ: Module nghiệp vụ về Quản lý Nhà hàng được lưu trữ trong  bộ nhớ (RAM), vì vậy triển khai FTGO lý tưởng là trên các máy chủ có nhiều RAM. Ngược lại, Module nghiệp vụ về xử lý hình ảnh sử dụng nhiều CPU và nên được triển khai trên các máy chủ có nhiều CPU. Vì các module nghiệp vụ này là một phần của cùng một ứng dụng nên FTGO phải thỏa hiệp về cấu hình máy chủ → Tức là để mở rộng FTGO, ta cần thuê thêm các máy chủ vừa nhiều RAM, vừa nhiều CPU. (Mặc kệ rằng chỉ có 1 số module nhất định bị thiếu tài nguyên).

 

Ứng dụng không đáng tin cậy (DELIVERING A RELIABLE MONOLITH IS CHALLENGING): Một vấn đề khác với ứng dụng FTGO là thiếu độ tin cậy. Một lý do khiến nó không đáng tin cậy là việc Testing một cách kỹ lưỡng rất khó do kích thước lớn của nó. Việc thiếu khả năng testing này có nghĩa là lỗi sẽ xâm nhập vào giai đoạn production. Tệ hơn nữa, ứng dụng thiếu khả năng cách ly lỗi (fault isolation) vì tất cả các thành phần đều chạy trong cùng một quy trình.

 

Bị trói buộc bởi Tech Stack lỗi thời (LOCKED INTO INCREASINGLY OBSOLETE TECHNOLOGY STACK): Kiến trúc monolith gây khó khăn cho việc áp dụng các framework hay ngôn ngữ mới. Sẽ cực kỳ tốn kém và rủi ro khi viết lại toàn bộ ứng dụng monolith để chuyển đổi sang một công nghệ mới và tốt hơn. Dev mãi mãi bị mắc kẹt với những lựa chọn công nghệ mà họ đã đưa ra khi bắt đầu dự án.

 

Vậy, liệu mô hình Microservice có giải quyết được các vấn đề ở trên. Hãy đón đọc ở các phần sau của chuỗi bài viết "Weekly MicroClosure" nhé.