Engineering the architecture behind uber's new rider app
Trong quá trình phát triển một vài ứng dụng cho hệ thống Taxi, tôi có tìm hiểu ứng dụng Uber và thấy bài viết của đội phát triển Uber khá thú vị. Ứng dụng Uber phát triển dựa trên concept đơn giản: nhấn nút và trả về chuyến đi. Uber khởi đầu bằng request xe cao cấp (xe màu đen), điều phối hàng ...
Trong quá trình phát triển một vài ứng dụng cho hệ thống Taxi, tôi có tìm hiểu ứng dụng Uber và thấy bài viết của đội phát triển Uber khá thú vị.
Ứng dụng Uber phát triển dựa trên concept đơn giản: nhấn nút và trả về chuyến đi. Uber khởi đầu bằng request xe cao cấp (xe màu đen), điều phối hàng triệu chuyến mỗi ngày trên khắp hàng trăm thành phố. Vì thế đội ngũ phát triển Uber xác định họ cần có một kiến trúc đủ mạnh cho đến 2017 và tương lai xa hơn nữa.
Nhưng bắt đầu từ đâu? Trở về thời điểm bắt đầu năm 2009, không có gì cả. Uber quyết định viết và thiết kế lại ứng dụng rider. Kết quả của lần cải cách này là Uber app mà bạn thấy ngày hôm nay. Phần dưới sẽ chỉ cho bạn tại sao Uber viết nên kiến trúc Riblets và họ đã đạt được mục tiêu bằng cách nào.
Trong khi kết nối app riders và hệ thống, Uber nhận thấy sản phẩm này còn phát triển lớn hơn nhiều và ứng dụng mobile có thể không theo kịp. Thách thức kỹ thuật và những hạn chế qua nhiều năm tích lũy lại khiến ứng dụng khó phát triển tốt với tính năng mới. Thêm nữa uberPool, lịch trình các order đang ngày càng trở nên phức tạp. Module trip phát triển lớn hơn, rất khó để kiểm thử. Những thay đổi nhỏ cũng ảnh hưởng đến phần khác trong app, việc này làm rối quá trình debug, và hạn chế tốc độ scale app đi. Để duy trì ứng dụng Uber chất lượng cao cho người dùng, Uber cần cơ chế để đơn giản hóa và giúp họ có thể đi được xa.
Reliablity is Core
Về mặt kĩ thuật, Uber phấn đấu đạt đến mức 99,99% độ chính xác cho phần core. Có nghĩa là chỉ cho phép 1 sai sót trong vòng 1 tuần, 1 sai sót trên 10,000 lần chạy.
Để đạt hiệu suất này, một cơ chế mới được định nghĩa và implement thành framework core và optional core. Core code- mọi thứ như sign up, gọi, hoàn thành hoặc cancel hành trình đều phải chạy tốt. Bất kể thay đổi nào của core đều phải trải qua quá trình review cẩn thận. Phần optional Code thì quy trình ít nghiêm ngặt hơn, đến mức có thể bật, tắt mà không ảnh hưởng đến tầng business. Việc này khuyến khích cô lập mã nguồn, cho phép Uber thử các tính năng mới, kể cả lúc nó chạy sai thì cũng không ảnh hưởng đến hành trình.
Rails for the Future
Uber cần một nền tảng mà từ đó hàng trăm đội với hàng ngàn kĩ sư có thể xây dựng các tính năng chất lượng một cách nhanh chóng mà không làm ảnh hưởng đến phần core. Vì thế Uber đã thống nhất một nền tảng dạng cross-platform, nơi mà cả kỹ sư iOS và Android có thể làm việc thống nhất.
Trong quá khứ, ứng dụng Android và iOS có phương pháp tiếp cận khác nhau về kiến trúc, thư viện và phân tích. Với kiến trúc mới, cả Android và iOS sẽ sử dụng cùng pattern và các practise across-platform. Điều này cho phép các kỹ sư tận dụng cơ hội học tập từ cả 2 nền tảng. Một lỗi thường gặp ở cả 2 nền tảng (vì chúng cùng chung cơ chế) giờ đây họ chỉ cần giải quyết vấn đề ở 1 nền tảng thì coi như đã giải quyết nốt ở nền tảng còn lại. Do đó, kỹ sư Android và iOS có thể cộng tác với nhau dễ hơn và phát triển tính năng mới hầu như song song.
Những điểm chung giữa 2 nền tảng mà kiến trúc mới yêu cầu:
- Kiến trúc core
- Tên class
- Quan hệ kế thừa (inheritance) giữa các đơn vị business
- Chia các đơn vị bisiness
- Chuỗi Reactive programming
- Unified platform components
Để đạt được mức thống nhất đến chi tiết như thế này, Uber đã phải tách các logic business, lưu lượng dữ liệu, định tuyế một cách rõ ràng. Hệ thống mới giúp tăng năng suất kĩ thuật, dễ kiểm thử, dễ mở rộng hơn.
From MVC to Riblets
Mô hình cũ của Uber là MVC. Trong quá trình xem xét các mô hình khác, họ tìm thấy VIPER, dùng nó để tạo nên Riblets. Cốt lõi của Riblets là định hướng theo business logic thay vì view logic.
Bắt đầu từ MVC
Ứng dụng Uber đầu tiên được tạo bởi số ít lập trình viên từ 4 năm trước, nhưng mô hình này khó mở rộng. Giống như trước kia, đội kĩ sư tiếp tục làm việc trên mô hình MVC. Khi lên đến hàng trăm kĩ sư cùng làm việc, họ nhận thấy, MVC thực sự không thể mở rộng và theo kịp tốc độ phát triển của họ. Họ phải đối mặt với 2 vấn đề lớn:
Đầu tiên: MVC thường phải đối mặt với lượng code tầng controller khổng lồ. Ví dụ RequestViewController bắt đầu với 300 dòng code, nhưng nó đã vượt 3000 dòng, phải xử lý quá nhiều logic business, thao tác với dữ liệu, logic network, logic định tuyến, data verifi... Nó trở nên khó đọc và khó sửa.
Thứ hai, MVC có nhược điểm rất dễ bỏ sót kiểm thử mỗi khi update thêm process. Hãy tưởng tượng RequestViewController và và TripViewController ở thời điểm hiện tại, mỗi lần thêm một cặp if else vào, thật nhiều rủi ro! Chưa kể mỗi lần bổ sung tính năng mới và phát triển mô hình Uber. Kết luận, loại kiến trúc này không phải dành cho hệ thống cần mở rộng.
Along the way: VIPER
Uber xem xét để mở rộng, thay thế mô hình MVC, họ đã lấy cảm hứng từ VIPER có thể được sử dụng như Clean Architecture cho iOS app. VIPER có một số lợi thế hơn MVC. Đầu tiên, nó cho phép nhiều lớp trừu tượng hơn.
Riblets: Uber’s Rider App Architecture
Trong kiến trúc mới sử dụng Riblets, logic được chia ra thành từng phần nhỏ, độc lập với quá trình kiểm thử, đến mức mỗi thành phần có một mục đích duy nhất. Uber sử dụng Riblets như từng mảng module và toàn bộ ứng dụng được cấu trúc như 1 cây Riblets.
Riblets and Their Components
Uber phân tách thành 6 component khác nhau để thêm logic business và view logic:
Điều gì phân biệt VIPER và MVC? Định hướng được dựa trên logic business hơn là logic hiển thị ở view. Điều này có nghĩa là các ứng dụng được điều khiển bởi dòng chảy của thông tin và các quyết định thay vì ảnh hưởng từ phần view. Tại Uber không phải mảng logic nào cũng được người dùng nhìn thấy. Thay vì cố gắng đặt một cách vụng về logic business vào 1 ViewController trong MVC chúng ta có thể tạo Riblets riêng biệt cho mỗi phần của logic business, việc này đem lại mỗi thành phần Riblets đều trở nên có ý nghĩa. Mỗi thành phần Riblets được tạo bởi 1 Router, 1 Interactor, 1 Builder với Component, 1 Presenters và Views. Tầng Router, Interactor xử lý phần business logic, trong khi tầng Presenter và View thì xử lý phần logic views.
Xem xét từng đơn vị Riblet, qua ví dụ về Product Selection Riblet:
Builder
Khởi tạo tất cả các Riblet chính và định nghĩa các phụ thuộc. Trong ví dụ về
Product Selection Riblet thì Builder định nghĩa dòng dữ liệu từ luồng dữ liệu của thành phố phụ thuộc(luồng dữ liệu cho thành phố cụ thể).
Component
Component bao gồm services, data streams và những thứ không thuộc Riblet builder. Với Product Selection Component thì Component bao gồm và khởi tạo những phụ thuộc của stream data thành phố đó, đưa vào từng sự kiện thích hợp và inject vào Interator.
Routers
Tầng Routers từ tầng application làm công việc attaching hoặc detaching con Riblets. Tầng Routers định hướng vòng đời Interator. Routers gồm 2 thành phần của business logic:
- Helper method làm công việc attaching/detaching
- Logic chuyển đổi trạng thái để điều khiển các thành phần con
Trong Product Selection Riblet thì không có Riblets con. Tầng Routers của cha là Confirmation Riblet thì quyết định attaching thành phần Product Selection Riblet và thêm Views vào View hierarchy. Sau đó, mỗi sản phẩm được chọn thì Product Selection’s Router sẽ deactive Interator của nó.
Interactors
Tầng Interactors thực hiện logic business. Ví dụ:
- Tạo services calls để khởi tạo hành động, giống như request 1 hành trình.
- Tạo services calls để fetch data
- Xác định trạng thái tiếp theo, ví dụ nếu Interactor báo xác nhận của người dùng không hợp lệ, nó sẽ gửi 1 yêu cầu đến Router của mình để chuyển sang trạng thái "Welcome".
Product Selection Riblet Interactors gồm dữ liệu của service của thành phố: thông tin về giá, ước lượng thời gian, views phương tiện...Nó gửi thông tin đến tầng Presenter. Nếu người dùng click vào uberPool đến uberX, tầng Interactor nhận thông tin từ Presenter sau đó request dữ liệu tương ứng và gửi sang tầng View để hiển thị. Trong ngắn hạn, Interactor thực hiện tất cả các logic nghiệp vụ mà sẽ được trình bày ở tầng Views.
View (Controller)
Tầng views xây dựng và update thông tin lên UI, bao gồm khởi tạ các thành phần UI, hiển thị data và animation.
Presenter
Tầng presenter quản lý thông tin liên lạc giữa Interactors và Views. Từ tầng Interactors đén Views, tầng Presenter chuyển business model vào trong objects để Views có thẻ hiển thị. Với Product Selection nó bao gồm giá, views phương tiện. Từ tầng Views đến Interactors, Presenter chuyển từ user interaction event sang logic chọn sản phẩm, tương ứng với thành phần trong Interaction.
Reference
https://eng.uber.com/new-rider-app/