5 poin oleh GN⁺ 2023-12-02 | 1 komentar | Bagikan ke WhatsApp
  • Aplikasi frontend yang kompleks berawal dari cache respons API, lalu memikul indeks manual hingga invalidasi cache, sehingga kondisinya mendekati implementasi ulang database kecil di setiap proyek
  • Dalam framework deklaratif seperti React, agar API tidak dipanggil di setiap render, cache ditempatkan di state lokal atau lapisan Redux, dan lapisan ini lambat laun mengambil peran sebagai penyimpanan pusat
  • Penyimpanan berbasis ID dan struktur kueri berdasarkan tanggal membuat pencarian lebih cepat, tetapi menjaga konsistensi di antara beberapa struktur seperti CACHE dan ENTRIES_BY_DATE menambah beban pengujian dan code review
  • Perubahan optimistis langsung memperbarui UI sebelum respons server sehingga meningkatkan kecepatan yang dirasakan, tetapi membawa biaya konsistensi seperti duplikasi logika klien-server, pelacakan perubahan yang sedang berjalan, rollback saat error, dan rekonsiliasi setelah aplikasi dijalankan ulang
  • SQLSync berupaya menyediakan cache yang tahan lama, indeks, constraint, perubahan optimistis, dan query reaktif di dalam stack frontend melalui database lokal berbasis SQLite dan sinkronisasi mirip Git Rebase

Proses cache frontend berkembang menjadi database

  • Pengelolaan data frontend dapat dimulai dari cache sederhana yang menyimpan respons API ke variabel lokal
    • Framework deklaratif seperti React me-render ulang tree berkali-kali selama interaksi pengguna
    • Agar tidak mengirim request API di setiap render, hasil request atau error dapat disimpan dalam state komponen dengan useState dan useEffect
    • Contoh disederhanakan demi kejelasan, dan dalam praktiknya ada juga pilihan untuk memakai library API yang sudah teruji
  • Cache dapat dipindahkan ke lapisan yang lebih tinggi dalam tree UI atau ke luar UI
    • Redux adalah library manajemen state React yang menyatukan state dan mengoordinasikan perubahan atomik dari waktu ke waktu
    • Ekosistem Redux telah berkembang dengan tool dan pola untuk mengelola caching data API
    • Cara penggunaan seperti ini bertujuan memusatkan logika cache, mengoordinasikan pembaruan, dan berbagi hasil cache antar-komponen
  • Semakin besar lapisan caching, semakin ia menyerupai sistem penyimpanan pusat yang menangani data secara efisien sesuai mesin rendering dan aksi pengguna

Indeks manual dan beban konsistensi

  • Di frontend pun, data yang diterima dari server dapat disimpan dalam objek dengan ID sebagai key agar cepat dicari dan diubah
    • Pada aplikasi yang memakai REST API, data sering dibaca secara batch lalu objek yang dibutuhkan diperkaya lagi
    • Menyimpan objek per ID memudahkan penggabungan hasil API ke cache
    • Struktur ini dioptimalkan untuk operasi create, read, update, dan delete berbasis ID
  • Filtering yang harus menelusuri banyak item mendorong pembuatan indeks terpisah untuk menghindari pemeriksaan seluruh entri
    • Jika membuat struktur ENTRIES_BY_DATE berdasarkan bagian tahun/bulan/tanggal dari createdAt, entri pada tanggal tertentu dapat ditemukan dengan cepat
    • Sebagai gantinya, konsistensi antara CACHE dan ENTRIES_BY_DATE harus terus dijaga
    • Kueri rentang tanggal membutuhkan beberapa kali akses, dan array yang diurutkan berdasarkan tanggal dengan logika kueri dan pembaruan yang lebih kompleks bisa menjadi struktur yang lebih baik
  • Semakin banyak indeks, setiap indeks membutuhkan logika pembuatan, pembaruan, dan kueri
    • Verifikasi kebenaran membesar menjadi beban pengujian dan code review
    • Jika penghapusan atau pembaruan terlewat di satu indeks, bug yang sulit ditemukan dapat muncul
    • Lebih banyak waktu dapat tersita untuk infrastruktur yang mengelola kompleksitas ini daripada untuk fitur aplikasi baru
  • Indeks database sungguhan jauh lebih kompleks daripada sekadar menyimpan data dalam bentuk lain
    • Di dalamnya termasuk elemen seperti pengumpulan statistik, manajemen versi data, kontrol transaksi, locking, dan interaksi dengan optimisasi kueri

Masalah konsistensi yang ditambahkan oleh perubahan optimistis

  • Perubahan optimistis terlebih dahulu menyimulasikan efek operasi tertentu secara lokal sebelum respons server datang
    • UI dapat bereaksi seketika seolah tidak ada latensi jaringan
    • Jika server mengambil keputusan berbeda dari perkiraan atau terjadi error, UI mungkin perlu me-rollback perubahan dan meminta pengguna memperbaiki masalah
    • Ini menjadi tool yang kuat ketika klien dapat memprediksi hasil server dengan baik, error dapat ditangani di klien, dan logikanya tersinkronisasi erat
  • Update optimistis biasanya mengalir dalam empat tahap
    • UI memicu operasi tulis
    • Dengan asumsi server akan menyetujuinya, perubahan diterapkan ke cache lokal dan UI langsung di-render ulang
    • Operasi perubahan dikirim ke server secara asinkron
    • Respons server digabungkan ke cache lokal untuk menimpa perubahan optimistis sebelumnya, lalu UI di-render ulang jika diperlukan
  • Untuk menjaga konsistensi dengan server, beberapa beban muncul
    • Logika harus diduplikasi di klien dan server agar hasil dapat diprediksi
    • Setiap perubahan yang sedang berjalan harus dilacak untuk menangani error asinkron atau ketidaksesuaian server
    • Demi pengalaman pengguna yang lebih baik, bagian cache optimistis mungkin perlu dibuat tahan lama agar perubahan dapat direkonsiliasi setelah aplikasi dijalankan ulang
  • Proses ini meningkatkan waktu pengembangan dan biaya verifikasi kebenaran, sehingga pengelolaan data dapat mendahului pengembangan nilai bagi pengguna atau fitur pembeda

Kompleksitas invalidasi cache rekursif

  • Dalam aplikasi dengan banyak data, informasi yang sama muncul di berbagai lokasi dalam cache
    • Contoh cache menyimpan projects, tasks, dan users bersama-sama
    • Setelah tugas selesai, beberapa bagian seperti progres proyek, tugas yang ditugaskan ke pengguna, dan informasi tugas baru dapat terdampak
  • Untuk menyelaraskan cache dengan server setelah tugas selesai, beberapa round trip mungkin diperlukan
    • Memberi tahu server bahwa tugas telah selesai
    • Me-refresh proyek karena progres proyek berubah
    • Memeriksa apakah ada penugasan tugas baru
    • Jika ada penugasan baru, mengambil tugas tersebut
  • API yang lebih kompleks dapat mengurangi jumlah round trip, tetapi hasilnya tetap membuat API atau logika klien terikat dengan model data yang mendasari
    • GraphQL adalah salah satu pendekatan untuk masalah ini, tetapi bukan solusi lengkap
  • Struktur yang mengharuskan UI mengetahui bagian cache mana yang terkait pada setiap perubahan menjadi rapuh seiring skala bertambah
    • Relasi dan agregasi data dapat memengaruhi banyak bagian cache lokal
    • Ketika tim engineering membesar, masalahnya dapat melintasi batas tim dan terasa seperti variabel global mutable dalam proyek software besar
  • Jika digabungkan dengan perubahan optimistis, klien akan mereplikasi lebih banyak logika backend untuk memprediksi perubahan server
    • Dalam contoh, klien mungkin mencoba menghapus task 1 dari user 1 dan menghitung progres sebagai rasio baru menggunakan total jumlah tugas
    • Semakin nested perubahan yang dibuat agar dapat diprediksi secara lokal, semakin banyak stack backend yang diduplikasi oleh klien

Stack database frontend yang diusulkan SQLSync

  • SQLSync adalah stack database yang dioptimalkan untuk frontend dan dibangun di atas SQLite
    • Mesin sinkronisasinya didasarkan pada gagasan dari Git dan sistem terdistribusi
    • Dirancang untuk terintegrasi mulus dengan framework frontend populer seperti React, Vue, dan Next.js
    • Tujuannya menangani masalah pengelolaan data yang sulit agar developer dapat fokus pada fitur khas aplikasi
  • Contoh aplikasi Todo mengimplementasikan seluruh lapisan data dengan 60 baris Rust dan beberapa query SQL yang tersebar di komponen
    • SQLSync menyediakan cache tahan lama, indeks/constraint/trigger/optimisasi kueri SQLite, perubahan optimistis, invalidasi cache cerdas, dan query reaktif
  • Data lokal disimpan dalam satu atau lebih database SQLite
    • Indeks dapat dibuat dengan mudah dan otomatis tersinkronisasi dengan data
    • Database dapat otomatis menggunakan indeks untuk mempercepat kueri seperti di backend
    • SQL dapat mengekspresikan pengambilan data yang kompleks, dan fitur seperti triggers, foreign keys, constraints, serta full-text search juga dapat digunakan
  • Perubahan optimistis ditangani dengan reducer
    • Strukturnya mirip dengan konsep inti Redux
    • Reducer dapat ditulis dalam bahasa apa pun yang dapat dikompilasi ke WebAssembly
    • SQLSync menjalankan perubahan secara optimistis di klien dan menjalankannya di server dalam urutan yang konsisten secara global
    • Setelah itu, klien tersinkronisasi dengan server melalui operasi yang mirip Git Rebase
  • Arsitektur ini memiliki keunggulan karena menghilangkan kebutuhan invalidasi cache rekursif
    • Semua logika perubahan data ditulis dalam reducer yang mudah dibagikan di klien dan server
    • Semua perubahan data yang terjadi selama perubahan tersebut otomatis terlihat
    • Karena sinkronisasi bekerja seperti Git Rebase, bahkan jika server membuat perubahan berbeda dari klien, klien dijamin mencapai hasil konsisten yang sama

Pekerjaan terkait

  • “Building data-centric apps with a reactive relational database” dari Riffle membahas ide menyimpan seluruh state aplikasi, termasuk state UI, dalam satu database reaktif
    • Query reaktif menyediakan model berpikir yang rapi dan cocok dengan sistem deklaratif seperti React
    • Masalah pengembangan aplikasi klien diselesaikan dengan gagasan dari komunitas database
    • Membahas manfaat memodelkan state dengan model data relasional dan indeks sungguhan
  • Stepan dari Instant.db menulis dua artikel tentang database di dalam browser
    • Database in the Browser, a Spec
    • A Graph-Based Firebase
    • Kedua artikel membahas masalah serupa dengan lebih berfokus pada hubungan antara stack frontend dan backend, serta menjelaskan motivasi di balik pembuatan Instant.db sebagai penerus berbasis graf dari Firebase
  • CR-SQLite dari Matt Wonlaw adalah ekstensi SQLite
    • Menggunakan conflict-free replicated data type (CRDT) dan log peristiwa berurutan kausal untuk menggabungkan data secara konsisten
    • Memungkinkan aplikasi peer-to-peer menyimpan data di SQLite dan berkolaborasi tanpa koordinator pusat
    • Ini juga merupakan contoh menjalankan SQLite di browser
  • Matt Wonlaw juga sedang mengeksplorasi gagasan terkait

1 komentar

 
GN⁺ 2023-12-02
Pendapat di Hacker News
  • Saya cukup mengenal proyek ini, dan pembuatnya teman saya, jadi saya akan coba mengajaknya ke sini untuk menjawab pertanyaan
    Dia adalah arsitek basis data yang berpengalaman. Dengan SQLsync, ia membuat developer frontend bisa melakukan query dan update ke basis data jarak jauh seolah-olah basis data itu sepenuhnya berada di dalam browser. Pada praktiknya memang hampir begitu, dan berkat WASM, seluruh basis data SQLite bisa dikirim ke browser. Intinya ada pada algoritme reaktif yang cerdas tetapi sederhana untuk sinkronisasi di banyak klien
    Kalau kita menganggap sebagian besar pekerjaan pengembangan adalah sinkronisasi data, React dan REST API juga bisa dilihat sebagai semacam prosedur sinkronisasi, dan pendekatan ini membuka kemungkinan baru. Tidak perlu lagi membuat basis data kustom yang aneh berupa tree objek yang diambil dari API lalu di-cache; cukup gunakan kekuatan basis data relasional untuk langsung melakukan update dan query secara lokal

    • Menurut saya memindahkan seluruh sistem query ke frontend adalah titik yang sebenarnya diinginkan banyak developer frontend. Mereka menginginkan sistem query yang kuat atas data, bukan terus-menerus menciptakan ulang lapisan transport, REST, GraphQL, *RPC, dan semacamnya
      Namun di perusahaan web tradisional, ini sulit diadopsi karena adanya tim backend/frontend yang terspesialisasi. Ini seperti membuang lapisan basis data, backend, transport, dan autentikasi lalu menggantinya dengan satu sistem blok; kebanyakan arsitek sistem berasal dari backend sehingga tidak terlalu memahami masalah ini. Karena menyentuh kedua sisi secara mendalam, pendekatan ini tidak cocok dengan sistem yang sudah ada, dan pada akhirnya lebih cocok untuk pengembangan baru. Backend-nya juga bukan layanan AWS atau Azure dan tidak ramah Lambda, jadi sebagian besar tipe arsitek yang saya temui kemungkinan tidak akan mau menyentuhnya
      Cara ini sampai batas tertentu sudah ada dengan teknologi lama, CouchDB+PouchDB. Untuk sebagian penggunaan, itu cukup cocok, tetapi sistem query-nya tidak ideal, dan cara autentikasi serta penentuan cakupan datanya terasa asing bagi kebanyakan orang. Kasus termudah adalah ketika data sepenuhnya dimiliki satu pengguna dan model basis data per pengguna bisa dipakai apa adanya. Jika data dipartisi kuat dengan CRDT, masalah konflik juga jauh berkurang
      Namun ada masalah skalabilitas. Ketika 10 ribu hingga 100 ribu pengguna terhubung, kebutuhan CPU CouchDB sangat tinggi; teknologinya juga sudah tua, meski masih dipelihara. Dari sisi desain sistem, begitu mulai berbagi data antar pengguna, kompleksitas meningkat tajam, sehingga alih-alih menyelesaikan kompleksitas, kita hanya memindahkan lokasinya, dan kecocokannya pun menurun
      Pendekatan ini tampaknya juga mengincar tujuan yang sama, tetapi kemungkinan besar akan mengalami masalah skalabilitas serupa. Saya menantikan bagaimana ini berkembang ke depan; ini terlihat seperti langkah awal
    • Ini terdengar cukup mirip dengan Couchbase, yang memungkinkan query/update basis data yang disinkronkan secara remote lalu disinkronkan lagi ke peer. Autentikasi atau logika bisnis juga bisa dikendalikan dengan mudah lewat plugin JavaScript di sisi server
    • Saya benar-benar penasaran, kenapa tidak cukup meng-cache bagian yang relevan di LocalStorage / SessionStorage?
      Saya ingat dulu Chrome pernah mencoba memasukkan basis data SQL secara harfiah ke browser, tetapi tidak berjalan baik, lalu localStorage menjadi arus utama. Saya tidak bermaksud meremehkan kegunaannya; biasanya saya cenderung memilih apa yang disediakan browser. Saya sangat menantikan kemungkinan membawa WASM, beserta ekosistemnya yang makin matang atau fiturnya yang bertambah, ke browser
  • Perusahaan tempat saya bekerja dulu menggunakan software manajemen proyek yang memiliki mekanisme check-out/check-in untuk perubahan. Saat sebuah proyek di-check-out, salinan untuk diedit secara lokal diunduh; saat di-check-in, salinan itu diunggah kembali ke server. Selama status check-out, proyek dikunci. Di era aplikasi dengan update live, semua orang merasa itu cara yang ketinggalan zaman
    Namun setelah 10 tahun membuat web app SPA, cara sinkronisasi data itu terasa seperti mendahului zamannya

    • Apakah akan mendukung update live paralel dari banyak pengguna, atau menguncinya agar hanya satu update berjalan pada satu waktu, pada akhirnya bukan keputusan teknis melainkan keputusan bisnis. Intinya adalah apakah aturan bisnis yang cocok untuk aplikasi tersebut memungkinkan penanganan update live secara bersamaan
      Pada akhirnya, ini bermuara pada apakah kita bisa mengimplementasikan prosedur yang secara konsisten menyelesaikan ketidaksesuaian di antara beberapa update bersamaan. Ada kasus yang memungkinkan dan ada yang tidak, dan itu lebih bergantung pada aturan bisnis daripada kemampuan teknis
      Jika berdasarkan aturan bisnis kita tidak bisa mengimplementasikan mekanisme penyelesaian, maka meski secara teknis mampu mendukung update bersamaan, tetap diperlukan penguncian agar hanya satu update terjadi pada satu waktu
    • Dengan cara ini, banyak masalah terselesaikan dan implementasinya juga menjadi sangat mudah
      Namun sulit meyakinkan orang bahwa inilah yang sebenarnya diinginkan. Mudah sekali terjebak dalam ilusi besar bahwa semuanya harus selalu tersedia, padahal dalam kenyataan biasanya satu orang membuat perubahan pada satu waktu, dan jika dua orang atau lebih harus bekerja, mereka sering kali tetap perlu berbicara atau berkomunikasi untuk menyelaraskan
      Bahkan dalam pengembangan yang sepenuhnya terdistribusi seperti Git, konflik tidak bisa diselesaikan otomatis secara ajaib. Untuk memilih perubahan yang benar, kita tetap perlu berkomunikasi dengan orang lain dan memahami konteksnya
    • Justru karena hal ini, paradigma basis data relasional klasik seperti MySQL tetap bertahan meskipun orang merendahkannya dibanding basis data non-relasional seperti NoSQL atau MongoDB. Tidak semua hal bisa digantikan hanya karena lebih cepat atau lebih keren
      Beberapa hal membutuhkan solusi yang sudah teruji
    • Terdengar seperti RCS https://en.wikipedia.org/wiki/Revision_Control_System
      Saya ingat dulu saat perusahaan pindah dari RCS ke CVS, salah satu rekan kerja kesal karena CVS tidak mendukung check-out dengan penguncian
      https://en.wikipedia.org/wiki/Concurrent_Versions_System
    • Saya suka pendekatan ini. SQLSync pada dasarnya melakukan ini terus-menerus, tetapi dengan koordinasi eksplisit, pola check-in/check-out semacam itu juga seharusnya bisa dilakukan
      Saya rasa strategi penguncian pemilik tunggal juga bisa disimulasikan dengan SQLSync. Namun tergantung aplikasinya, itu mungkin tidak diperlukan. Jika tujuannya adalah bekerja offline lalu menggabungkan perubahan saat siap, SQLSync menyediakan pola ini secara bawaan. Jika tujuannya hanya memperbolehkan satu klien melakukan perubahan, diperlukan pola penguncian terpusat, dan kemungkinan ini juga bisa dikoordinasikan melalui SQLSync
  • Di sini, prinsip “apa yang diukur akan dikelola” terkait dengan kekeliruan biaya hangus
    Masalah sebenarnya pada database adalah kompleksitas. Tiap fitur secara terpisah umumnya aman, tetapi saat reliabilitas, caching, dan indeks saling berinteraksi, kompleksitasnya meledak, dan biasanya mengimplementasikan DB khusus domain tidak masuk akal
    Namun ketika perusahaan menyadari bahwa mereka sudah berinvestasi dan menggelontorkan banyak sumber daya untuk mengimplementasikan ketiga fitur itu, secara politis sulit untuk menyarankan agar semuanya dicabut, dan biaya nyata untuk menghapus utang teknis sekaligus juga besar
    Menurut saya masalah sebenarnya adalah sintaks SQL. Jika pengalaman memakai database relasional dasar terasa senyaman sintaks bergaya C yang familier, bukan bahasa Inggris patah-patah, insentif untuk memakai DB alih-alih membuat sendiri akan lebih besar. Database NoSQL adalah langkah bagus ke arah itu, tetapi umumnya terlalu berfokus pada big data ketimbang kegunaan sehari-hari. Yang seperti Redis sudah mapan dan cukup bagus
    Membuat SQL mudah dijalankan adalah pendekatan yang masuk akal, tetapi database yang bagus, misalnya Postgres yang saya suka, memakai SQL sebagai bahasa bawaan, sehingga sulit mendapatkan efisiensi tanpa memakai bahasa itu. Kita benar-benar butuh database seperti PostgresPostSQL yang mereplikasi Postgres secara sempurna, tetapi parser bawaannya mendukung bahasa dengan sintaks yang bagus

    • Saya tidak tahu persis apa yang sulit dari SQL. Menurut saya semua developer harus tahu SQL. Sintaks SQL juga bagus dan sudah terbukti cukup kuat untuk bertahan lama. Daripada menyalahkannya, akan lebih membantu jika benar-benar meluangkan waktu untuk mempelajarinya
    • SQL sering dikritik, dan menurut saya ada alasan yang layak untuk itu, tetapi mengapa kita belum bisa membuat sesuatu yang lebih baik?
      Dalam pemrograman umum, puluhan bahasa digunakan dan terus berevolusi. Bahkan JavaScript, yang sulit diubah karena dijalankan browser dan kita tidak bisa mengendalikan browser pengguna, terus berevolusi lewat transpiler dan WebAssembly
      Namun di database, pada praktiknya hanya ada satu SQL. Ada alternatif, tetapi dari sisi penggunaan tidak ada yang mendekati SQL. Mungkin saja SQL memang tidak seburuk itu
      Alasannya mungkin karena model relasional memang sangat bagus. Upaya untuk keluar dari sini kemungkinan besar hanya berhasil di ceruk tertentu. Gaya deklaratif juga sangat bagus, dan keluar dari sana pun sulit meraih keberhasilan besar. Pada akhirnya, jika yang dibuat hanya SQL dengan sintaks berbeda, bagi kebanyakan orang peningkatannya tidak cukup besar untuk mengganti cara kerja
    • Saya berharap Postgres punya API yang lebih stabil dan lebih rendah levelnya daripada SQL. Bentuknya mungkin mirip dengan rencana kueri yang diperoleh dari EXPLAIN
      Aplikasi yang ditulis untuk API ini dapat mengimplementasikan DB SQL. Cukup parsing SQL, lalu implementasikan perencana kueri yang mengeluarkan rencana kueri yang sesuai dengan API ini
    • Saya penasaran apakah Anda pernah melihat pendekatan menambahkan lapisan semantik di atas database untuk orang-orang yang ingin menghindari SQL secara langsung
  • Saya penulisnya. Saya baru sempat membaca sekilas sebagian besar pertanyaan, dan akan terus memeriksa secara berkala apakah ada pertanyaan yang terlewat. Saya juga penasaran apakah ada yang sudah membuat cara yang lebih baik untuk melacak diskusi HN
    Saya sangat senang dengan diskusi sejauh ini. Tulisan pertama lebih berfokus pada motivasi rekayasa frontend yang membuat saya membangun SQLSync, daripada bagaimana SQLSync bekerja secara konkret. Saya berencana membahas cara kerjanya di tulisan berikutnya

    • Saya penasaran apakah ada rencana dukungan mobile. Di mobile-lah saya paling ingin mencoba ini
  • Jangan sampai pengguna memiliki model mental yang dapat dirusak oleh realitas secara parah, atau tanpa terlihat
    Saya khawatir pendekatan menyinkronkan database alih-alih model client-server adalah salah satu kasus seperti itu. Mekanisme sinkronisasinya bisa saja langsung meleleh, atau mungkin ada asumsi-asumsi mendalam yang tidak terpenuhi
    Jika membutuhkan UI cepat, rasanya lebih aman membuat sekumpulan primitif CRDT dan memakainya, sementara sisanya tetap berupa pengiriman formulir

    • Setuju. Salah satu tujuan kami adalah membuat developer yang memakai SQLSync dapat memahami model mentalnya dengan mudah. Mungkin saya bias, tetapi secara pribadi saya merasa model rebase jauh lebih mudah dipahami daripada CRDT
  • Sinkronisasi state antara client dan server adalah masalah terkutuk
    Dengan menerima sedikit pengorbanan pada pengalaman pengguna dan kembali ke pendekatan yang lebih dekat dengan model PHP/server-side rendering, masalah ini bisa dihindari sepenuhnya. SPA memang bagus, tetapi pengiriman formulir multipart juga masih berfungsi. Dengan JavaScript yang sangat sedikit pun, sebagian besar sisi kasar yang tersisa bisa dihaluskan
    Pada produk web terbaru, state di sisi client hanya klaim autentikasi dari IdP pihak ketiga, ID sesi pihak pertama di parameter kueri, dan dokumen saat ini. Sejujurnya saya juga tidak tahu yang pertama disimpan di mana. Itu masalah Microsoft, bukan masalah kami. Semua state lainnya ada di server
    Kami memperlakukan client seperti terminal bodoh yang hanya mencetak input sepanjang hari. Kami juga tidak memakai cookie pihak pertama atau local storage. Pendekatan ini sangat memperbaiki pengalaman pengembangan untuk target iOS/Safari
    Jadi saya ingin bertanya, pengalaman apa sebenarnya yang ingin diberikan, dan mengapa itu cukup membenarkan pemisahan state client dan server

    • Dalam antarmuka grafis untuk konsumen, rendering optimistis adalah alur yang sangat umum, dan jika melakukannya, berarti Anda berurusan dengan state client/server. Saya masih melihat spinner loading di sana-sini, tetapi biasanya untuk pemuatan konten awal. Misalnya Gmail tidak membuat pengguna menunggu saat mengarsipkan email
    • ElectricSQL telah membuat kemajuan besar untuk menyelesaikan masalah ini. Ia menulis ke SQLite di client dan menjamin sinkronisasi ke Postgres
      Referensi: https://news.ycombinator.com/item?id=37584049
  • Tampaknya pendekatan berbasis SQLite yang offline/local-first sedang hangat belakangan ini. Ini yang ketiga saya baca minggu ini, dan kelihatannya bagus
    Tapi bagaimana dibandingkan dengan ElectricSQLhttps://electric-sql.com/ dan PowerSynchttps://powersync.com/?

    • Ini memang bidang yang benar-benar sedang panas. Sangat menarik melihat berbagai pendekatan yang berbeda bermunculan
      ElectricSQL dan PowerSync sama-sama menangani masalah yang sangat sulit, yaitu replikasi parsial. Mereka mencoba membuat solusi umum agar database pusat tradisional dapat menyinkronkan dua arah hanya data yang dibutuhkan klien, sambil tetap mendukung perubahan optimistis beserta penanganan konsistensi/konflik yang mengikutinya
      Kekurangannya adalah kompleksitas implementasi. Kita harus melacak dengan tepat subset mana dari keseluruhan database yang dimiliki tiap klien, supaya perubahan bisa didorong hanya ke subset tersebut. Selain itu, untuk menentukan subset mana dari status database yang akan diunduh, dibutuhkan DSL baru, yang juga harus dipelajari dan dioptimalkan. Meski begitu, menyenangkan melihat mereka memecahkan masalah yang sangat sulit ini, dan saat SQLSync siap mendukung replikasi parsial, praktik terbaiknya kemungkinan sudah tersusun
      Sebaliknya, SQLSync saat ini hanya mendukung sinkronisasi seluruh DB. Semua klien melihat tampilan yang konsisten atas seluruh database. Bisa langsung muncul pertanyaan apakah ini ide yang baik, dan memang tidak cocok untuk sebagian aplikasi. Namun jika membayangkan aplikasi keuangan pribadi, tujuan utamanya adalah sinkronisasi antarperangkat, backup cloud, kemampuan offline, dan semacamnya, sehingga menyimpan seluruh DB di semua perangkat justru bisa menjadi cara yang diinginkan. Model data berorientasi dokumen seperti Airtable juga bisa menjadi contoh. Jika tiap Airtable dijadikan database terpisah, klien bisa mengelola sendiri tabel mana yang perlu diperhatikan
      Dengan berfokus pada sinkronisasi seluruh DB, mesin sinkronisasinya menjadi jauh lebih sederhana dibandingkan solusi yang mendukung replikasi parsial. Salah satu keuntungannya adalah backend bisa sangat ringan. Demo saat ini (https://sqlsync-todo.pages.dev) berjalan sepenuhnya di dalam Cloudflare Durable Objects dengan penggunaan storage dan waktu CPU yang sangat kecil
      Masih banyak yang harus dilakukan agar SQLSync dapat memungkinkan use case seperti ini, dan statusnya masih lebih dekat ke prototipe, tetapi pengujian awalnya sangat positif
  • Dalam aplikasi multitenant besar dengan dataset individual yang relatif kecil, saya beberapa kali berpikir, “bagaimana kalau database-nya langsung saja dikirim ke klien?” Saya tidak mendalaminya karena terlihat seperti pola arsitektur terkutuk yang cukup jauh menyimpang dari standar. Akan menyenangkan kalau ternyata saya salah

    • Saya pernah melakukannya sekali pada aplikasi penghitung kalori. Meski databasenya berisi ratusan ribu makanan, aplikasinya tetap memakan ruang jauh lebih sedikit daripada kebanyakan aplikasi media atau game
    • Saya juga penasaran apakah ini cara yang terkutuk. Sejauh ini hasilnya jauh lebih baik dari dugaan. Aplikasi seperti https://sqlsync-todo.pages.dev menjadi trivial dengan pola ini
      Masih banyak yang harus dilakukan untuk membuktikannya dengan benar, tetapi saya cukup antusias untuk terus mendorongnya dan melihat ke mana arahnya
    • Jawaban yang benar adalah mengembalikan UI ke server tempat database memang sudah berada, lalu hanya mengirim HTML ke renderer HTML sisi klien, yaitu web browser. Seluruh tulisan ini kurang lebih seperti “frontend sudah melewati batas, melampaui tebing, lalu jatuh ke laut”
  • Ini terlihat seperti salah satu masalah yang akan sepenuhnya hilang jika kita meninggalkan SPA
    Dengan solusi keluarga Hotwire atau htmx, kueri hanyalah kueri server, dan masalah membuat kueri semacam itu cepat sudah jauh lebih dipahami

    • Ini bukan hanya masalah website. Apakah ekosistem mobile dan desktop juga harus banyak bergerak ke arah thin client seperti browser? Apakah aplikasi sederhana seperti Apple Reminders atau Google Tasks harus membuat GUI-nya macet karena latensi atau masalah koneksi?
    • Belakangan ini saya mencoba htmx, dan kompleksitas yang hilang serta produktivitas yang dihasilkannya benar-benar luar biasa. Bisa tetap memakai stack yang diinginkan juga merupakan berkah besar
      Saya memakainya bersama ocaml + web components, dan itu pengalaman produktivitas 10/10. Cukup punya satu build tool yang kompilasinya lebih cepat daripada kedipan mata, dan karena tidak perlu wiring mapping JSON antara frontend dan backend, rasanya benar-benar produktif
    • Sejujurnya solusi seperti Hotwire atau Livewire tidak seinstan SPA
      Secara pribadi saya lebih suka InertiaJs https://inertiajs.com. Itu semacam sistem frontend router yang menyinkronkan state dengan server memakai “cara lama”
    • Itu mirip mengatakan “jangan membuat webapp yang sangat interaktif” atau “jangan mengoperasikan layanan yang butuh lebih dari satu VM”. Pandangan yang tidak masuk akal
    • Menurut saya masalahnya hilang bukan saat kita menjauh dari SPA, melainkan saat kita menjauh dari fitur dengan interaksi tinggi yang sering dimiliki produk-produk yang enak digunakan
      Apalagi jika produknya harus tetap berjalan di wilayah dengan internet yang tidak stabil
  • Saat ini saya sedang menulis artikel yang sangat mirip tentang “database full-stack”. Artikel itu membahas pola banyak aplikasi yang membuat ulang logika backend dan database di kode klien frontend. Solusi yang kami sarankan adalah memilih database yang bisa dijalankan di sisi server maupun klien, lalu menyinkronkannya di antara keduanya
    Alasan kami tidak memakai SQLite di produk kami adalah, jujur saja, SQL bukan alat yang cocok untuk melakukan kueri pada data aplikasi. Ia tidak mudah selaras dengan struktur data yang diinginkan di kode klien, dan hampir semua database SQL tidak punya cara untuk berlangganan perubahan kueri tanpa melakukan polling kueri berulang-ulang
    Jika Anda menyukai ide menaruh database lengkap di klien dan menginginkan integrasi mendalam dengan TypeScript/JavaScript, silakan lihat https://github.com/aspen-cloud/triplit yang sedang kami buat

    • Sebenarnya Postgres menyediakan cara yang sangat baik untuk berlangganan perubahan real-time melalui WAL. Saya juga mengelola library open source ini
      https://github.com/cpursley/walex
    • SQLite sebenarnya menyediakan mekanisme yang diperlukan untuk mendeteksi perubahan melalui update hook. Sayang banyak binding SQLite tidak mengeksposnya
      Saya memakainya dengan cara yang sangat sederhana. Jika data pada tabel dasar berubah, kueri otomatis dijalankan ulang. Ini mungkin tidak seefisien memperbarui hasil secara inkremental, tetapi karena kueri SQLite biasanya sangat cepat, menurut saya itu bukan masalah besar
    • Saya tidak setuju bahwa ia tidak mudah selaras dengan struktur data yang diinginkan di kode klien. Normalisasi data penting untuk aplikasi frontend yang reaktif, dan pada dasarnya diperlukan jika ingin menjaga data tetap terbaru. Semua operasi CRUD juga menjadi jauh lebih mudah ditangani
    • Dengan Realm Sync dan Mongo + Kotlin Multiplatform, hampir semua platform seperti server, web, mobile, dan desktop bisa dicakup. Tentu saja ada biayanya. Saya sebenarnya tertarik pada alternatifnya, dan penasaran apakah hal ini juga akan masuk ke artikel tersebut