12/08/2018, 15:06

Sự thay đổi cấu trúc của Uber đang diễn ra

Uber được xây dựng trên một khái niệm đơn giản: bấm nút, gọi xe. Với bắt đầu chỉ là cách để request một xe sang cao cấp giờ đã cung cấp cho hàng loạt sản phẩm, điều phối hàng triệu chuyến đi mỗi ngày trên hàng trăm thành phố. Ban điều hành Uber đang xác định lại kiến trúc di động của mình để hỗ trợ ...

Uber được xây dựng trên một khái niệm đơn giản: bấm nút, gọi xe. Với bắt đầu chỉ là cách để request một xe sang cao cấp giờ đã cung cấp cho hàng loạt sản phẩm, điều phối hàng triệu chuyến đi mỗi ngày trên hàng trăm thành phố. Ban điều hành Uber đang xác định lại kiến trúc di động của mình để hỗ trợ cho thực tế đó, hiện tại và tương lai

Bắt đầu lại? Uber sẽ trở lại nơi họ bắt đầu vào năm 2009: không gì cả. Uber quyết định sẽ viết và thiết kế lại hoàn toàn ứng dụng của họ. Bắt đầu từ đâu? Uber sẽ trở lại nơi họ bắt đầu vào năm 2009: không gì cả. Uber quyết định sẽ viết và thiết kế lại hoàn toàn ứng dụng của họ. Kết quả là ứng dụng mới được thực hiện trên một kiến trúc mới trên cả iOS và Android. Có tên Riblets, sẽ giúp họ đạt được mục tiêu của mình

Động lực: Đến đâu?

Khi mà kết nối giữa người đi với hệ thống giao thông theo yêu cầu vẫn còn là ý tưởng, sản phẩm của Uber đã phát triển thành một thứ lớn hơn, và kiến trúc ban đầu không thể theo kịp Thách thức kỹ thuật tích tụ qua nhiều năm ngày một nhiều khi họ mở rộng ứng dụng để phù hợp với những tính năng mới. Những bổ sung như uberPOOL, chuyến đi đặt lịch trước hay chế độ xem xe khuyến mại đã cho thấy sự phức tạp. Các module ngày một lớn, trở nên khó kiểm soát. Sự kết hợp những thay đổi nhỏ tạo ra thách thức phá vỡ các phần khác, sinh ra nhiều bug, ngăn cản tốc độ phát triển trong tương lai. Để duy trì trải nghiệm Uber chất lượng cao cho mọi người dùng, họ cần tìm cách khôi phục lại sự đơn giản ban đầu, khi tính đến vị trí hiện tại và nơi muốn đến trong tương lai

Tin cậy là cốt lõi

Nhìn từ khía cạnh kỹ thuật, họ đang cố gắng để tạo độ tin cậy Uber thực tế đạt 99,99% số lần khi trải nghiệm chuyến đi. Đạt được độ tin cậy này nghĩa là họ chỉ có tổng 1h ngừng hoạt động 1 năm, 1 phút mỗi tuần hay 1 thất bại trong 1 vạn lượt chạy

Rails cho tương lai

Họ cần một nền tảng từ đó đội ngũ hàng trăm team lập trình khác nhau và hàng ngàn lập trình viên có thể xây dựng các tính năng chất lượng nhanh chóng sáng tạo trên top của ứng dụng mà không ảnh hưởng đến trải nghiệm cốt lõi. Họ đưa ra nền tảng kiến trúc mới, để cả kỹ sư iOS và Android đều có thể làm việc thống nhất.

Về lịch sử, những ứng dụng tốt nhất trên iOS và Android liên quan đến cách tiếp cận khác nhau đối với kiến trúc, thiết kế thư viện và phân tích. Ở kiến trúc mới, sử dụng các mô hình tốt nhất trên cả hai nền tảng. Thay vì mắc lại lỗi như nhau vì phân tách từng team theo từng nền tảng, bài học từ một nền tảng có thể giải quyết vấn đề ở nền tảng kia. Vì vậy, các kỹ sư iOS và Android, có thể hợp tác với nhau dễ dàng hơn và giải quyết những tính năng mới song song

Mặc dù có những trường hợp các nền tảng có thể khác nhau (như giao diện người dùng chẳng hạn), nền tảng của cả iOS và Android đều bắt đầu từ những điểm nhất quán

Kiến trúc cốt lõi Tên class Quan hệ kế thừa giữa các đơn vị business logic Cách chia business logic các Plugin point (tên, cấu trúc..) các thành phần nền tảng thống nhất

Để đạt các thiết kế chung giữa các nền tảng, kết trúc mới yêu cầu tổ chức rõ ràng và phân tách business logic, luồng dữ liệu, và routing. Kiến trúc đó làm giảm sự phức tạp, đơn giản hóa khả năng kiểm tra, từ đó tăng hiệu suất kỹ thuật và độ tin cậy người dùng.

Từ MVC đến Riblets

Với mục đích đó, họ đã khảo sát những kiến trúc cũ và điều tra các option để nâng cấp thêm. Cơ sở mã kế thừa từ điểm bắt đầu của Uber theo mô hình MVC hay VIPER. Và cuối cùng tạo ra Riblet. Sự đổi mới cốt lõi với Riblet là định tuyến theo business logic thay vì view logic.

MVC (Model-View-Controller)

Ứng dụng trước đây được xây dựng dựa trên mô hình MVC với bắt đầu chỉ vài kỹ sư. Khi team làm việc tăng lên vài trăm người, mô hình MVC có một số vấn đề: kiến trúc MVC thường phải đối mặt với lượng view controller khổng lồ. ví dụ, RequestViewController bắt đầu với 300 line code, nay đã có hơn 3000 line do phải xử lý nhiều trách nhiệm: business logic, thao tác dữ liệu, xác nhận dữ liệu, logic network, logic định tuyến, etc. Trở nên khó đọc và thay đổi

kiến trúc MVC có quá trình cập nhật yếu, thiếu sự kiểm tra. Khi thử nghiệm rất nhiều trước khi đưa ra tính năng mới cho người dùng. những test if-else. Bất cứ khi nào có class nhiều chức năng, các lệnh if-else lồng nhau, sẽ gần như không thể để kiểm tra đơn lẻ. Ngoài ra, sự tách rời code như RequestViewController và TripViewController ngày một lớn, khiến việc cập nhật ứng dụng trở nên khó khăn hơn. Vì họ cần thử nghiệm để tiếp tục thêm những tính năng mới và phát triển kinh doanh, nên kiến trúc kiểu này không ổn

VIPER

VIPER cho thấy một số ưu điểm hơn so với MVC. Đầu tiên, nó cung cấp nhiều abstraction hơn. Presenter gồm những logic trình bày gắn kết business logic với view logic. Interactor xử lý thao tác và xác minh dữ liệu thuần túy. Nó bao gồm việc tạo cuộc gọi dịch vụ tới hỗ trợ, như việc đăng ký hay tạo request. Và, Router đưa người dùng từ home đến màn hình xác nhận. Thứ hai, với cách tiếp cận của VIPER, Presenter và Interactor là những object cũ nên có thể sử dụng những unit test đơn giản.

Nhưng VIPER cũng có một số khuyết điểm. Logic ứng dụng kiểu view-driven nghĩa là trạng thái ứng dụng được điều khiển bởi view, vì toàn bộ ứng dụng được dựa trên cây view. business logic thực hiện bởi interactor được cho là trạng thái ứng dụng đều phải qua Presenter, vì vậy, có thể xót business logic. Và cuối cùng, với một vài cây view và business nhỏ, rất khó để thực hiện hoặc chỉ business logic hoặc trên view logic

Mặc dù VIPER cung cấp những cải tiến đáng kể so oviws MVC, nhưng cũng ko đáp ứng đầy đủ các nhu cầu và mục tiêu của Uber như một nền tảng có thể mở rộng với module rõ ràng. Họ phát triển một mô hình kiến trúc lấy những lợi ích của VIPER và giải quyết các khuyết điểm. Kết quả là Riblet

Riblets: Kiến trúc ứng dụng Rider của Uber

trong mô hình mới này, logic được chia nhỏ thành các phần nhỏ có thể test độc lập mà mỗi mục tiêu có một mục đích duy nhất, theo nguyên tắc trách nhiệm duy nhất. Sử dụng Riblet như các module, và toàn ứng dụng là cấu trúc như một cây Riblets.

Riblets và các thành phần có 6 thành phần khác nhau từ hoạt động kinh doanh trừu tượng đến view logic.

Sự khác biệt của Riblet với VIPER và MVC? Routing được điều khiển theo business logic hơn là view logic. Nghĩa là ứng dụng được điều khiển bởi luồng thông tin và các quyết định hơn là trình bày. Ở Uber, không phải mọi phần của business logic liên quan đến view người dùng thấy. Thay vì tích hợp business logic vào một ViewController như trong VMC hay thao tác trạng thái ứng dụng thông qua Presenter như trong VIPER, có thể tách Riblet thành từng phần business logic, theo từng nhóm logic có ý nghĩa và dễ giải thích. Họ thiết kế mô hình Riblet thành nền tảng để thống nhất phát triển trên cả iOS và Android

Builder Builder khởi tạo tất cả các đơn vị Riblet và xác định sự phụ thuộc. trong Product Selection Riblet, đơn vị này xác định luộng dữ liệu thành phố (dòng dữ liệu cho một thành phố cụ thể)

Component

Component nhận và khởi tạo sự phụ thuộc của Riblet. Điều này gồm các dịch vụ, luồng dữ liệu, và những gì không là đơn vị Riblet chính. Product Selection Component lấy và khởi tạo sự phụ thuộc luồng thành phố, nối nó với sự kiện network thích hợp và đưa nó vào Interactor

Routers Routers tạo nên cây ứng dụng bằng cách gắn và tách các Riblet con. Những quyết định này được thông qua bởi Interactor. Rounters cũng điều khiển vòng đời Interactor bằng cách kích hoạt và hủy chúng ở bộ chuyển mạch trạng thái. nó gồm 2 mảng business logic: Phương pháp trợ giúp để gắn và tách bộ định tuyến logic chuyển đổi trạng thái để xác định các trạng thái giữa nhiều Riblet con

Product Selection Riblet không có Riblet con nào. Confirmation Riblet chịu trách nhiệm gắn Product Selection’s Router và thêm View vào hệ thống View. Sau đó, khi một sản phẩm được lựa chọn, Product Selection Router sẽ hủy Interactor của nó.

Interactors

Interactors thực hiện những business logic. Bao gồm

Thực hiện cuộc gọi dịch vụ để khởi tạo hoạt động, như tạo request Thực hiện cuộc gọi dịch vụ để lấy dữ liệu.

Product Selection Interactor lấy luồng thành phố chữa dữ liệu gồm dịch vụ trong thành phố, thông tin giá, thời gian đi lại ước tính, và chế độ xem xe. Nó gửi thông tin này đến Presenter. Nếu người dùng nhấp từ uberPOOL sang uberX, Interactor nhận thông tin này từ Presenter. Nó tập hợp những dữ liệu liên quan để chuyển lại cho View để hiển thị xe uberX và ước tính thời gian. Tóm lại, Interactor thực hiện tất cả các business logic được hiển thị trên View

View (Controller) Tạo View và update UI, nhanh chóng tạo và đặt ra các UI component, xử lý tương tác người dùng, điền vào UI component với dữ liệu và animation. View cho Product Selection Riblet hiển thị object nó nhận được từ Presenter (tùy chọn, giá, ETA, xem xe trên bản đồ) và chuyển lại hành vi của người dùng (như lựa chọn sản phẩm)

Presenter

Presenter quản lý liên lạc giữa Interactors và Views. Từ Interactors đến Views, Presenter dịch mô hình kinh doanh sang các đối tượng sang đối tượng mà View có thể hiển thị. Với Product Selection, điều này bao gồm dữ liệu giá và xem xe. Từ View đến Interactor, Presenter dịch sự kiện tương tác người dùng như tap vào button để chọn sản phẩm thành các hành động thích hợp trong Interactor.

Tích hợp lại

Riblets chỉ có một cặp Router và Interactor duy nhất, nhưng chung có thể có nhiều phần view. Riblet có thể xử lý business logic và không có phần tử giao diện người dùng, không có phần view. Riblet có thể là single-view (một Presenter một View), multi-view (hoặc 1 Presenter nhiều View hay nhiều Presenter nhiều View) hoặc viewless (không Presenter không View). Điều này cho phép độ sâu và cấu trúc cây business logic khác với cây view, sẽ có hệ thống phân cấp. Nó giúp đơn giản hóa quá trình chuyển màn hình.

Ví dụ, Ride Riblet là một viewless Riblet để kiểm tra người dùng có chuyến đi đã kích hoạt hay ko. Nếu có lái xe, nó gắn Trip Riblet, hiển thị chuyến đi trên bản đồ. Nếu ko, nó gắn Request Riblet, hiển thị màn hình giúp người dùng yêu cầu 1 chuyến đi. Những Riblet kiểu này không có view logic phục vụ một chức năng quan trọng bằng cách phân tách business logic để điểu khiển ứng dụng, giữ tính module của kiến trúc mới này

Riblet xây dựng ứng dụng như thế nào

Riblet tạo nên cây ứng dụng và cần giao tiếp để cập nhật thông tin hoặc đưa người dùng đến giai đoạn tiếp theo trong việc di chuyển. Trước khi đi sâu vào cách chúng giao tiếp, trước tiên chúng ta hãy hiểu về luồng dữ liệu trong Riblet

Luồng dữ liệu trong Riblet

Interactor sử hữu trạng thái về phạm vi và business logic điểu khiển ứng dụng. Đơn vị này thực hiện gọi service để lấy dữ liệu. Trong kiến trúc mới, dữ liệu chạy theo một hướng. Nó đi từ dịch vụ đến model stream rồi đến Interactor. Interactor, trình lập lịch, thông báo từ network có thể yêu cầu dịch vụ và thay đổi model stream. Model stream tạo ra những mô hình bất biến. Nó thực thi những yêu cầu mà Interactor class phải sử dụng tầng dịch vụ để tạo thay đổi trạng thái ứng dụng

Luồng ví dụ:

Từ dịch vụ backend đến View: Cuộc gọi dịch vụ, giống như trạng thái, lấy dữ liệu từ backend. Nó đưa dữ liệu vào một dòng mô hình bất biến. Một Interactor lắng nghe luồng này thông báo dữ liệu mới và chuyển nó đến Presenter. Presenter định dang dữ liệu và gửi đến View

Từ View đến backend: Người dùng nhấp vào 1 button, như đăng nhập chẳng hạn, và View gửi tương tác này đến Presenter. Presenter gọi phương thức đăng nhập trên Interactor. Token được trả về được xuất hiện trên luồng nhờ dịch vụ. Interactor lắng nghe luồng và chuyển sang Home Riblet

Liên lạc giữa các Riblet

Khi Interactor đưa ra quyết định business logic, nó cần thông báo cho Riblet khác về sự kiện và gửi dữ liệu. Để được như vậy, Interactor tạo quyết định business logic và gọi giao diện ứng với Interactor của một Riblet khác.

Thông thường, nếu liên lạc từ cây Riblet đến Interactor của Riblet cha, giao diện được hiểu như một listener. Listener luôn thực hiện bởi Interactor của Riblet cha. Nếu liên lạc xuống một Riblet con, giao diện được hiểu như một delegate, thực hiện bởi Interactor của Riblet con. Delegate chỉ có ý nghĩa với liên lạc trực tiếp giữa các đơn vị Riblet, như Interactor cha với con chẳng hạn.

Đặc biệt với liên lạc xuống các Riblet con, Riblet cha có thể chọn hiển thị luồng dữ liệu đến Interactor của Riblet con. Interactor của Riblet cha có thể gửi dữ liệu đến Interactor Riblet con thông qua luồng này.

Bằng cách cấu trúc luồng dữ liệu trong và giữa các Riblet theo cách này, đảm bảo dữ liệu đến đúng màn hình đúng lúc. Bởi các Riblet hình thành cây ứng dụng dựa trên business logic, có thể định tuyến liên lạc thông qua business logic hơn là qua view logic. Điều này có ý nghĩa lớn với hoạt động kinh doanh và giúp phát triển ứng dụng trở nên không quá phức tạp

Trở về điểm xuất phát

Làm thế nào để tăng tính sẵn dùng cho trải nghiệm chuyến đi?

Riblet phân tách trách nhiệm rõ ràng, nên kiểm tra sẽ đơn giản theo. Mỗi Riblet được kiểm tra độc lập. Với kiểm định tốt hơn, Uber có thể tự tin hơn về độ tin cậy của ứng dụng khi triển khai bản cập nhật. Vì mỗi Riblet có một trách nhiệm duy nhất và dễ dàng để tách riêng Riblet và sự phụ thuộc của chúng với nhân (cần thiết để đăng ký và đi xe UberPOOL hay uberX)

Điều này cũng cho phép roll-back luồng chính đến trạng thái làm việc được đảm bảo. tất cả các mã tùy chọn có cờ hiệu năng có thể được tắt nếu phần đó bị lỗi. Trường hợp xấu, có thể tắt tất cả mã tùy chọn và giữ lại miên lõi. điều này đảm bảo luồng lõi luôn hoạt động mọi lúc

Làm thế nào để thiết lập tập rails đúng cho việc phát triển app trong tương lai

Riblet giúp chúng ta thu hẹp và tách riêng chức năng càng nhiều càng tốt. Sự rõ ràng trong việc phân tách business logic và view logic giúp ngăn chặn codebase từ sự phát triển phức tạp và giúp dễ dàng phát triển hơn. Kể từ khi kiến trúc mới là nền tảng đồng nhất, kỹ sư iOS và Android có thể dễ dàng hiểu cách thức phát triển còn lại, học tập từ lỗi của phía kia và phát triển cùng nhau. Trải nghiệm sẽ gây ra ít ảnh hưởng đến kinh nghiệm lõi vì Riblet giúp tách biệt mã tùy chọn với mã lõi. Có thể thử nghiệm tính năng mới, phát triển như các plugin, mà không phải lo lắng việc uberX hay uberPOOL có vô tình dính lỗi từ nó hay ko

Tiến về phía trước

Có rất nhiều khả năng khả thi để phát triển theo kiến trúc mới này - cải thiện nguồn dữ liệu mới, mở rộng kiến trúc này cho người lái và UberEATS app, xây dựng cho các builder. Thực tế, họ đã dành vài tháng để tạo bản prototype, đảm bảo rằng họ có cân bằng phù hợp. Đem đến tương lai trải nghiệm Uber cho mọi người thông qua cả Android và iOS

From https://eng.uber.com/new-rider-app/

0