Linear đã đưa tôi đi vào con hố thỏ local-first
(bytemash.net)- Việc dùng Linear đã làm thay đổi đáng kể cách nhìn của tôi về cách phát triển ứng dụng web
- Linear hoạt động theo cách tiếp cận local-first, mang lại phản hồi tức thì và tương tác không có độ trễ mạng
- Cách tiếp cận này cho phép client có cơ sở dữ liệu độc lập, và các thay đổi được đồng bộ với máy chủ theo cách bất đồng bộ
- Tuy nhiên, đồng bộ trong môi trường phân tán, giải quyết xung đột, xử lý ngoại tuyến và các phần tương tự có độ khó triển khai rất cao
- Hệ sinh thái local-first có nhiều giải pháp mới như Jazz, Electric SQL, Zero đang xuất hiện, và trải nghiệm của nhà phát triển cũng ngày càng được cải thiện
Tổng quan
Khi dùng Linear, một công cụ quản lý dự án, tôi thấy được cách tiếp cận local-first mang lại tốc độ và trải nghiệm người dùng vượt trội. Việc gần như không hề cảm nhận được độ trễ mạng, trạng thái loading, hoặc refresh trang—những điều vốn rất quen thuộc ở ứng dụng web truyền thống—là điều ấn tượng nhất. Nhờ trải nghiệm này, tôi đã đi sâu hơn vào nguyên lý kỹ thuật cùng các ví dụ áp dụng thực tế của local-first.
Đi sâu vào con đường rabbit hole
Khi tìm hiểu bí mật kỹ thuật của Linear, tôi phát hiện họ đang tận dụng IndexedDB của trình duyệt như một cơ sở dữ liệu thật sự. Mọi thay đổi đều được xử lý trước tiên ngay tại local và sau đó đồng bộ nền bằng GraphQL và Websockets.
- Thuật ngữ local-first có thể được diễn giải theo nhiều cách, từ chiến lược UX (đáp ứng tức thì) đến triết lý lưu trữ dữ liệu tại local rồi đồng bộ
- Trong ứng dụng web truyền thống, máy chủ là nguồn dữ liệu duy nhất, nhưng trong kiến trúc local-first, mỗi client đều có cơ sở dữ liệu riêng
- Khi vị trí cơ sở dữ liệu được đưa về gần người dùng hơn, độ trễ mạng trong tương tác người dùng được loại bỏ hầu như hoàn toàn
Thách thức: Điều này không hề đơn giản
Khi thử triển khai trực tiếp cách làm của Linear, tôi nhận ra mức độ phức tạp là rất lớn.
- Xử lý chuyển đổi offline/online
- Giải quyết xung đột giữa các client phân tán
- Đồng bộ một phần (để tránh phải tải về toàn bộ dữ liệu)
- Di chuyển schema dữ liệu cache
- Bảo mật và kiểm soát truy cập trong môi trường phân tán
- Đây là những mảng cần đầu tư lượng thời gian và công sức kỹ thuật lớn
Hệ sinh thái local-first năm 2025
Tính đến năm 2025, hệ sinh thái local-first có nhiều giải pháp mạnh.
- Electric SQL: cơ chế đồng bộ dựa trên Postgres
- PowerSync: giải pháp theo hướng doanh nghiệp
- Jazz: công cụ giúp xây dựng ứng dụng local-first dễ như thao tác cập nhật trạng thái
- Replicache: một giải pháp chủ lực trước đây (đã đóng phát triển)
- Zero: hướng đi mới của đội ngũ Replicache
- Triplit: đồng bộ dựa trên TripleStore
- Instant: tập trung vào trải nghiệm nhà phát triển
- LiveStore: cung cấp lớp đồng bộ thời gian thực
Khám phá sâu: Jazz
Jazz gây chú ý nhờ cam kết đặc biệt: “tạo ứng dụng local-first dễ như cập nhật state”.
Mô hình tư duy
Jazz giới thiệu Collaborative Values (CoValues), một cấu trúc hợp tác thời gian thực.
- Định nghĩa schema bằng Jazz và Zod: định nghĩa này không đơn thuần là kiểu dữ liệu mà hoạt động như đối tượng sống tự động đồng bộ
- Không cần route API riêng, logic request/response hay DTO; chỉ cần thay đổi trạng thái của object là đồng bộ tự động
Jazz đạt được điều này như thế nào
- Bảo đảm tính duy nhất: gán tự động ID duy nhất cho từng dữ liệu
- Event sourcing: lưu lịch sử thay đổi dưới dạng sự kiện, tối ưu hóa hiệu quả đồng bộ thời gian thực
- Mã hóa đầu cuối: dữ liệu được mã hóa trên client trước khi đồng bộ; máy chủ chỉ thấy blob đã mã hóa
- Thiết kế quyền theo nhóm: thay vì ACL truyền thống, quyền được chia theo nhóm với quyền sở hữu được tách biệt rõ ràng
Các đánh đổi
Cấu trúc này đặc biệt hiệu quả cho prototyping và phát triển UI nhanh. Tuy nhiên, nó có một số điểm cần cân nhắc.
Máy chủ của bạn là mù
Vì mã hóa đầu cuối nên máy chủ không thể đọc dữ liệu người dùng. Nếu không định nghĩa rõ ràng trước dữ liệu nào cần server truy cập, việc quản lý như giám sát hoặc ngăn chặn lưu trữ độc hại sẽ bị hạn chế
Time Travel là bắt buộc
Nhờ event sourcing, mọi thay đổi đều được lưu lại vĩnh viễn. Điều này làm Undo/Redo rất tiện lợi, nhưng khi phải tuân thủ yêu cầu pháp lý như GDPR thì việc xóa dữ liệu lại khó khăn
Bộ nhớ cứ tăng
Việc không xóa khiến dung lượng lưu trữ tăng dần. Với dự án nhỏ thì ổn, nhưng với SaaS quy mô lớn chi phí lưu trữ có thể tăng đáng kể
Local Dev vẫn còn quirks
Xác thực dựa trên Passkeys là mặc định. Với tự triển khai hoặc môi trường local, những phiền toái ban đầu như HTTPS, quản lý chứng chỉ và chuyển khóa có thể xuất hiện. Tuy nhiên, các cải tiến như tích hợp Better Auth đang được lên kế hoạch
Nhưng thành thật mà nói? Vẫn đáng làm
Dù có các ràng buộc này, trải nghiệm phát triển và năng suất của Jazz là rất ấn tượng. Mặc dù hiện vẫn ở giai đoạn đầu, dự kiến các vấn đề khác nhau sẽ dần được giải quyết
Khám phá: Electric SQL và Zero
Khác với Jazz, Electric SQL và Zero theo cách tiếp cận từng bước.
- Có thể dùng trực tiếp các bảng Postgres hiện có
- Electric SQL cho phép đồng bộ giao diện người dùng bằng cách subscribe một phần bảng dưới dạng reactive query (Shape)
- Cách xử lý mutation của Electric SQL khác với Jazz, và có nhiều lựa chọn như tích hợp LiveStore
- Zero tương tự Electric nhưng có sẵn hỗ trợ đồng bộ hóa thay đổi
Khi nào local-first là phù hợp?
Phù hợp:
- Công cụ sáng tạo (thiết kế, viết lách, âm nhạc,...)
- Ứng dụng hỗ trợ cộng tác
- Ứng dụng di động cần hỗ trợ ngoại tuyến
- Công cụ dành cho nhà phát triển
- Ứng dụng năng suất cá nhân
Thách thức:
- Logic nghiệp vụ phía máy chủ quy mô lớn
- Yêu cầu kiểm toán (audit) nghiêm ngặt
- Hệ thống phân tích dữ liệu khối lượng lớn
- Hệ thống tích hợp sâu với các nền tảng sẵn có
- Hệ thống thường xuyên từ chối yêu cầu tại server
Nhìn về phía trước
Local-first hàm ý một cuộc chuyển đổi trong kiểu kiến trúc phát triển web. Linear đã chứng minh hiệu quả rõ rệt trên trải nghiệm người dùng. Nhà phát triển cần đánh giá xem các đánh đổi kiến trúc này có phù hợp với dự án của mình hay không.
Tôi đang tự làm ứng dụng cá nhân bằng Jazz để trực tiếp trải nghiệm những ưu điểm, nhược điểm và giới hạn trừu tượng hóa. Hệ sinh thái vẫn còn ở giai đoạn đầu, và kỳ vọng công cụ cùng pattern sẽ ngày càng trưởng thành, cải thiện dần. Tuy nhiên, lợi thế của việc để dữ liệu ở local là rất rõ ràng và có vẻ không biến mất.
Nếu có thể chấp nhận ràng buộc trong dự án mới, thì việc trải nghiệm local-first là đáng giá. Trường hợp tệ nhất, bạn học được một mô hình kiến trúc mới; trường hợp tốt nhất, bạn có thể xây dựng trải nghiệm người dùng nhanh đến mức gần như không thể làm được trước đây. Trong cuộc đua đáp ứng trong 300ms, đây là lợi thế lớn.
Chưa có bình luận nào.