Local-First Software & CRDT: Xây dựng app offline-first, sync không cần server trung tâm

Local-first software là triết lý thiết kế ứng dụng mà dữ liệu chính lưu trữ trên thiết bị người dùng trước, không phụ thuộc server trung tâm. Sync tự động qua cloud khi có kết nối – nhưng app vẫn hoạt động đầy đủ offline. CRDT (Conflict-free Replicated Data Type) là “bộ não” cho local-first: đảm bảo dữ liệu đồng bộ đúng ngay cả khi hai người chỉnh sửa cùng lúc, không cần trung tâm quyết định.

Tại sao Local-First quan trọng?

Mô hình cloud-first hiện tại có ba vấn đề cốt lõi:

  • Vendor lock-in & shutdown risk: Evernote hạn chế free tier, Google Inbox shutdown, các service ngừng hoạt động = mất dữ liệu
  • Privacy & sovereignty: Dữ liệu nhạy cảm (health, finance, personal notes) gửi lên server bên thứ ba
  • Offline = useless: Máy bay, vùng núi, server maintenance – app trở thành brick

Local-first giải quyết cả ba: dữ liệu ở trong tay bạn, app hoạt động offline, sync khi muốn.

CRDT là gì?

Conflict-free Replicated Data Type (được đề xuất bởi Marc Shapiro, Nuno Preguiça, 2011) là cấu trúc dữ liệu đảm bảo eventual consistency mà không cần conflict resolution thủ công.

Ý tưởng cốt lõi: dữ liệu được thiết kế để merge một cách tự nhiên. Hai replica cùng sửa cùng lúc → merge → cả hai đều đồng thuận với kết quả → không cần server quyết định đúng/sai.

Hai họ CRDT chính

  • Op-based (operation-based): Gửi thao tác (add, delete, move) qua mạng. Cần reliable broadcast, định dạng operation thống nhất.
  • State-based (CvRDT): Gửi toàn bộ state hiện tại, merge bằng hàm merge(state_a, state_b). Đơn giản hơn nhưng bandwidth cao hơn.

Sơ đồ cơ chế merge CRDT giữa hai replica khi cùng sửa dữ liệu

Thực tế: CRDT trong ứng dụng

Text/Notes (Yjs, Automerge)

Google Docs, Notion dùng OT (Operational Transformation) – phụ thuộc trung tâm. Yjs và Automerge dùng CRDT:

  • Yjs: Y.Text dùng RGA (Replicated Growable Array), hiệu năng cao, binding cho React, ProseMirror, Monaco
  • Automerge: immutable state, dễ debug, hỗ trợ nested objects, binary encoding hiệu quả

Ví dụ Yjs với React:

import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'

const ydoc = new Y.Doc()
const provider = new WebsocketProvider('wss://sync.example.com', 'room-1', ydoc)
const ytext = ydoc.getText('note')

ytext.insert(0, 'Hello local-first!')
// Cập nhật tự động sync qua WebSocket đến các peer khác

Lists & Todos (Y.Array)

Y.Array hỗ trợ insert/delete/move với định dạng Riak DT. Khi Alice xóa item 3, Bob thêm item mới → merge hoàn toàn tự nhiên, không ai mất data.

JSON & Structured Data

Y.Map, Y.Array, Y.Text cho phép lưu JSON phức tạp. Automerge còn hỗ trợ schema migration, undo/redo.

Kiến trúc local-first app: local storage, background sync, cloud backup với CRDT

Stack công nghệ Local-First

Thành phần Công nghệ Vai trò
CRDT Engine Yjs, Automerge Thao tác dữ liệu conflict-free
Local DB SQLite, IndexedDB, RxDB Lưu trữ persistent trên device
Transport WebSocket, WebRTC, WebTransport Đồng bộ real-time giữa devices
Cloud Backup WebDAV, S3, custom API Lưu snapshot, cross-device restore
Identity Ed25519 keypair, DID Peer ID không cần centralized auth
Encryption E2EE trước khi sync Server không đọc được data

Khi nào dùng Local-First?

  • Notes & knowledge base: Obsidian, Notion alternative, Obsidian Sync is cloud-first nhưng có plugins local-first
  • Task management: Todo app, project management – dữ liệu phải offline
  • Collaborative editing: Design tool, whiteboard, code editor – Google Docs nhưng local-first
  • IoT & Edge: Sensor data log, offline-first industrial app
  • Health & Finance: Privacy-sensitive data không muốn gửi cloud

Hạn chế & Lưu ý

  • Bandwidth: State-based CRDT gửi full state → không phù hợp large binary (dùng op-based hoặc delta sync)
  • Memory: Yjs history giữ toàn bộ operations → cần compaction định kỳ
  • Auth: E2EE key management phức tạp cho multi-device
  • Binary assets: Ảnh/video nên dùng file sync riêng (rsync, Syncthing) thay vì CRDT

Kết luận

Local-first không phải trend nhất thời – nó phản ứng lại mối quan hệ lệ thuộc mù mịt giữa người dùng và cloud vendor. Với CRDT, sync “just works”: merge tự nhiên, không cần central server quyết định đúng sai, offline vẫn full feature.

Stack hiện tại (Yjs + SQLite + WebSocket + WebDAV) đã đủ cho hầu hết use-case. Không cần chờ “perfect solution” – bắt build local-first ngay hôm nay, dữ liệu của bạn (và người dùng) sẽ cảm ơn bạn sau 5 năm.

Nguồn: Ink & Switch – Local-first Software, Yjs Documentation, Automerge, Yjs Research Paper, Kleppmann – CRDT Paper

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Debugging là gì: quy trình, kỹ thuật và công cụ tìm lỗi

Debugging là gì: quy trình, kỹ thuật và công cụ tìm lỗi phần mềm Debugging là quá trình phát hiện, cô lập và khắc phục lỗi trong phần mềm. Từ…

Xem thêm

Terraform là gì: công cụ Infrastructure as Code của HashiCorp

Terraform là gì: công cụ Infrastructure as Code của HashiCorp Terraform là một công cụ Infrastructure as Code (IaC) do HashiCorp phát triển, cho phép người dùng khai báo và…

Xem thêm

Fortran là gì: ngôn ngữ lập trình cho tính toán khoa học

Fortran là gì: ngôn ngữ lập trình cho tính toán khoa học Fortran là gì? Đây là ngôn ngữ lập trình cao cấp được thiết kế riêng cho các bài…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất