Frontend Du Ký mùa đầu tiên kết thúc với sự ổn định của EGANY Apps. Ở mùa mới, mình sẽ cùng các bạn khám phá quá trình phát triển Cross-platform Apps – một dự án bắt đầu từ vạch đích của EGANY.

Nếu bạn chưa đọc mùa 1, bạn có thể đọc tại đây.

Giới thiệu

Cross-platform apps

Cross-platform Apps sẽ được viết tắt bằng CPA để thuận tiện cho người viết bài

Một ngày đẹp trời, khi đang phơi đồ trên sân thượng thì bỗng dưng có sào quần áo rơi vào đầu sếp. Sếp đặt câu hỏi tại sao sào quần áo lại rơi vào đầu mình? Phải chăng do chỉ có một sào, quần áo quá nặng khiến cho sào quần áo sập xuống?

Bỗng nhiên sếp nhận ra rằng các ứng dụng của EGANY cũng giống như vậy. Các platform sẽ là sào quần áo và ứng dụng chính là các bộ quần áo đang được phơi. Nếu một ngày nào đó sào quần áo (platform) sập xuống thì quần áo cũng sẽ rơi rớt theo. Ấy vậy, sếp bắt đầu ý tưởng triển khai CPA – một "sàn ứng dụng nhà làm" của riêng EGANY.

CPA sẽ cho phép người dùng quản lý các ứng dụng chạy độc lập hoàn toàn trên tất cả các platform (Haravan, Sapo, Shopify, WordPress, LadiPage, …). Ứng dụng đầu tiên được phát triển là egaShop.

CPA ở thời điểm hiện tại vẫn là một dự án thử nghiệm với các tiêu chí sau:

  1. Độc lập nền tảng. Không phụ thuộc hoặc ít bị phụ thuộc vào API của platform
  2. Hiệu năng cao. Tăng tốc trải nghiệm của khách hàng bằng các công nghệ CDN, cache cũng như tối ưu hóa hiệu năng của ứng dụng
  3. Dễ dàng phát triển. Giải quyết các vấn đề liên quan tới UI component, tổ chức file tốt hơn

Bình mới

Bình mới

Nguồn ảnh: https://dribbble.com/shots/14903323-Cheers-to-2021

Khi team bắt đầu quá trình nghiên cứu cách thức hoạt động của các ứng dụng tương tự trên thị trường, HoverSignal là một trong những đối tượng team hướng tới. Qua tìm hiểu, HoverSignal hoạt động như sau:

  1. Khách hàng đăng ký website với HoverSignal
  2. Khách hàng cài đặt ứng dụng cho website đã đăng ký
  3. Khách hàng copy script được tạo bởi HoverSignal vào website của mình
  4. Ứng dụng được chạy theo thiết lập

Rất đơn giản phải không nào? Điều đặc biệt của HoverSignal là dù mình có cài một, hai hay nhiều ứng dụng thì website của mình cũng chỉ có một script duy nhất để chạy. Mình đặc biệt thích điều này vì khách hàng càng ít phải thao tác thì số lượng lỗi phát sinh càng thấp. Tuy nhiên, nhược điểm của HoverSignal là khi mình copy script qua một trang khác, các ứng dụng mình cài đặt vẫn chạy. Ngoài ra, do chỉ có một script để chạy toàn bộ các ứng dụng nên khi cài đặt từ ba ứng dụng trở lên thì hiệu năng của trang giảm đáng kể.

Team hoàn toàn có thể "tham khảo" cấu trúc của HoverSignal. Một vấn đề đang làm khó team là các ứng dụng của HoverSignal đa số sẽ chạy trên tất cả các trang của website nhưng ứng dụng của EGANY thì không như vậy. Ứng dụng của EGANY chỉ chạy trên một trang duy nhất và nếu làm theo mô hình của HoverSignal thì sẽ không phù hợp và làm giảm hiệu năng trang của khách. Hơn nữa, việc copy script qua website khác nhưng ứng dụng vẫn chạy là một điều team không muốn có. Dù luôn hướng tới lợi ích của khách hàng nhưng "không tiền thì cạp đất" phải không các bạn?

Sau nhiều ngày lặn lội và cài đặt đủ loại ứng dụng, không tìm được mô hình nào hoàn hảo 100%, team quyết định sử dụng mô hình của HoverSignal nhưng có một chút "biến tấu" nho nhỏ để phù hợp hơn với đặc thù ứng dụng của EGANY. Cụ thể:

  • Phát triển theo hướng HoverSignal (nhiều ứng dụng, một script)
  • Toàn bộ assets sẽ nằm ở phía EGANY thay vì nằm ở platform như trước
  • Xác thực người dùng qua URL để đảm bảo ứng dụng chỉ chạy trên một URL chỉ định trước

Team bắt đầu với cơ chế tạo ra script cho ứng dụng, tạm gọi là bundler. Bundler hoạt động khá đơn giản, mô hình bên dưới:

Bundler

  1. Core. Developer sẽ build sẵn file javascript để chạy ứng dụng và upload trước lên storage (nơi lưu trữ file của team). Bundler sẽ dựa vào thông tin của ứng dụng và tìm đúng core cần thiết
  2. Settings. Bundler sẽ được phía app service cung cấp các thông tin của ứng dụng, trong đó có thiết lập của người dùng. Thiết lập mặc định sẽ lưu tại database để dùng cho lần cài đặt đầu tiên
  3. Bundle. Kết hợp core và settings, ta có bundle hoàn chỉnh

Một ví dụ cụ thể hơn với code để các bạn dễ hình dung:

// Script để chạy ứng dụng
// core.js
console.log(settings.message);
// Thiết lập của người dùng
// settings.json
{
  "message": "Xin chào, Frontend Du Ký"
}
// File script sau khi được bundle
// bundle.js
const settings = { message: "Xin chào, Frontend Du Ký" };
console.log(settings.message);

// => 'Xin chào, Frontend Du Ký' được in ra khi ứng dụng chạy

Khi có được bundler hoàn chỉnh, công việc tiếp theo của team là làm sao sinh ra được một URL duy nhất cho từng website của khách nhưng vẫn đảm bảo được khi copy URL đó qua trang khác thì ứng dụng không chạy được. Team có hai phương án như sau:

  • Xác thực bằng API ở phía frontend. Khi chạy, ứng dụng sẽ gửi một dạng id cũng như URL của trang hiện tại, nếu phía backend kiểm tra hợp lệ thì ứng dụng mới được phép chạy tiếp. Ưu điểm của cách này là việc sinh script URL khá dễ dàng, chỉ cần id của khách hàng thôi cũng đủ xác thực rồi. Khuyết điểm là phía backend phải chịu tải thêm phần API xác thực. Ngoài ra, trong trường hợp ứng dụng không hợp lệ, script vẫn sẽ được tải và chạy khiến cho hiệu năng của website giảm đi
  • Xác thực trực tiếp ở phía backend. Backend sẽ thực hiện xác thực ngay khi nhận được request từ phía website của khách. Ưu điểm của cách này là chặn ngay từ phía backend, không cho phép người dùng có cơ hội "cheat" ở phía frontend. Khuyết điểm là phía backend cần có cơ chế sinh URL làm sao để không phải truy xuất thông tin ứng dụng của người dùng mỗi khi xác thực

Thảo luận một hồi lâu thì team chọn phương án thứ hai, xác thực ở phía backend. Team triển khai như sau:

  • Buộc khách hàng nhập trước URL khi cài đặt ứng dụng thay vì cài đặt rồi mới nhập URL của trang muốn chạy
  • Sử dụng thông tin URL đã nhập, hash URL và đính kèm vào script URL của khách. Vd: https://shop.dev/sales-001 sẽ có URL là https://cdn.egany.com/bundles/abc-xyz-123

Do cơ chế hash là một chiều (chỉ có thể hash từ 1 qua abc chứ không thể đi ngược lại từ abc ra 1) nên bundler bây giờ sẽ đảm nhận luôn việc xác thực script URL. Điều này khiến team khá lo ngại về khả năng chịu tải của server. Tuy nhiên, ưu tiên lớn nhất lúc bấy giờ vẫn là việc xây dựng MVP (Minimum Viable Product) để có thể đánh giá tính khả thi của giải pháp.

Ban đầu, team dự định sẽ tận dụng hệ thống có sẵn và chỉ bổ sung thêm bundler service mà thôi. Tuy nhiên, do tính ổn định của CPA chưa cao nên team quyết định xây dựng một hệ thống riêng biệt với hệ thống hiện tại để đảm bảo rằng việc phát triển CPA sẽ không ảnh hưởng tới hệ thống hiện tại. Việc này mất khá nhiều thời gian và công sức của team nhưng dưới góc nhìn của mình thì việc xử lý lỗi xung đột giữa hệ thống cũ và mới còn mất nhiều thời gian hơn nữa.

Rượu cũ

Rượu cũ

Nguồn ảnh: https://dribbble.com/shots/14132446-Vectober-07-Fancy

Bên trên là góc nhìn tổng quan về hệ thống. Liệu việc thay đổi hệ thống có ảnh hưởng tới hướng phát triển ứng dụng mà team vẫn đang làm hay không? Câu trả lời tất nhiên là có rồi. Tuy nhiên, team vẫn luôn hướng tới việc hạn chế tối đa thay đổi về cấu trúc ứng dụng để tránh việc phải phát triển song song hai phiên bản khác nhau của cùng một ứng dụng.

Về lý thuyết là vậy, tuy nhiên do dự án vẫn mang tính thử nghiệm nên team chưa tiến hành chuyển đổi các ứng dụng có sẵn. Thay vào đó, team sẽ phát triển một ứng dụng mới với tên gọi là egaShop. egaShop sẽ là tiền đề để chuyển đổi các ứng dụng sau này.

Như những ứng dụng cũ, egaShop vẫn:

  • Hỗ trợ website bán hàng. Mục đích của egaShop nói riêng và các ứng dụng khác tại CPA nói chung vẫn luôn là việc bổ sung tính năng, tăng trải nghiệm người dùng và tăng tỉ lệ chuyển đổi cho website
  • Core và Settings. Core và settings vẫn là hai thành phần chính của egaShop. Điểm khác biệt duy nhất là cách tổ chức thiết lập từ file sẽ chuyển thành dữ liệu tại database

Với hai lý do trên, việc phát triển ứng dụng gần như không có gì thay đổi, nếu không muốn nói là có phần tự do hơn vì ứng dụng bây giờ có thể chạy độc lập mà không hề phụ thuộc vào platform. Developer có quyền quyết định công nghệ mình muốn sử dụng để xây dựng ứng dụng. Đó có thể là React, Vue, Svelte hoặc đơn giản là JavaScript thuần túy.

Tuy nói là tự do công nghệ nhưng team vẫn gặp các vấn đề sau:

  • Kích thước bundle lớn. Kích thước của bundle khi cài đặt nhiều ứng dụng là rất lớn. Lý do là khi được build riêng, các ứng dụng sẽ không sử dụng lại phần chung mà mình có, ví dụ như React runtime. Việc này dẫn đến code dư thừa khá nhiều nếu có hai ứng dụng cùng dùng một công nghệ
  • Giới hạn đầu vào. Đầu vào (input) của bundler hiện tại chỉ hỗ trợ một file JavaScript duy nhất. Điều này phần nào cũng giới hạn developer về công nghệ sử dụng để gộp chung JS và CSS để đảm bảo đầu ra luôn là một file JS

Rất may mắn là team tìm ra được công nghệ phù hợp nên phần nào đó giải quyết được hai vấn đề trên. Tuy giải pháp này vẫn chưa triệt để nhưng nó vẫn đáp ứng tốt được nhu cầu của team.

Tạm kết

Gọi CPA là bình mới, rượu cũ không hề sai. Nhờ những nền tảng có được ở EGANY Apps, team chỉ cần đưa ra mô hình ứng dụng mới là gần như đã hoàn thành 50% công việc rồi. Dù vẫn gặp một chút khó khăn khi bắt đầu nhưng so với EGANY Apps thì vẫn dễ dàng hơn nhiều.

Ở tập sau, mình sẽ chia sẻ với các bạn một số công nghệ team lựa chọn để triển khai dự án CPA. Việc lựa chọn công nghệ sẽ đóng vai trò quyết định trong việc phát triển ứng dụng về sau – việc mà EGANY đã không làm tốt trong giai đoạn này.

Hẹn gặp các bạn ở tập Frontend Du Ký sau. Happy hacking!