Geçen Ekim ayında Lizbon'da bir otel odasında oturuyordum, ekibimin inşa etmek için dört ay harcadığı bir proje yönetim aracını tanıtmam gereken geceden bir gece önce. Otelin Wi - Fi'si şu şeyi yapıyordu:..ama aslında hiçbir şey yüklenmiyor. Ve uygulamamızı, gerçekten gurur duyduğum bu şeyi izledim, bir döndürücüyle boş bir ekran oluşturdum. Sonra bir zaman aşımı hatası. Sonra hiçbir şey.
Telefonumu çıkardım, cep telefonuna bağlandım ve zayıf bir bağlantı kurdum. Uygulama yüklendi, ancak her tıklama iki saniyelik bir bekleyişti. Bir görev mi oluşturdunuz? Çevirici. Sütunlar arasında bir görev taşınsın mı? Spinner. Orada oturup düşündüm: React'te bir ön uç, Node'da bir arka uç, bir Postgres veritabanı, bir Redis önbelleği, sadece görev panosu için altı çözümleyicili bir GraphQL API'si oluşturduk. Tüm bu altyapı ve kahrolası şey, 3.000 mil uzaklıktaki bir sunucuya gidiş - dönüş olmadan kendi verilerimi bana gösteremez.
O gece ciddi ciddi bakmaya başladığım geceydi.yerel - ilk mimari. Bir blog yazısı okuduğum veya bir tweet gördüğüm için değil.UtanmışHer gün
Bir şey hakkında açık olmak istiyorum: İlk yılı ya da daha fazlasını yerel olarak akademik olarak reddetmek için harcadım. OkudumMürekkep ve Anahtar "Yerel - İlk Yazılım" kağıdı2019 'da ortaya çıktığında şöyle düşündü:"Harika bir araştırma, gerçek uygulamalar için pratik değil ."Yanılmışım. 2019 'daki aletler gerçekten hazır değildi. Ama aynı zamanda tembeldim, zaten bildiğim mimariye göre tembeldim. Makale, yazılım için yedi ideal ortaya koydu:hızlı, çoklu cihaz, çevrimdışı, işbirliği, uzun ömür, gizlilik, kullanıcı sahipliği. Ve bunların mühendislik gereksinimleri değil, istek listesi gibi geldiğini düşündüğümü hatırlıyorum.
Yedi yıl sonra, yerel ilk kalıpları kullanarak üç üretim uygulaması gönderdim. Ayrıca, yanlış çağrı olduğu iki projeden ilk olarak yerel olanı söktüm. Fikirlerim var. Bazıları muhtemelen yanılıyor. Ama hak edildiler.
İşte 2026 'da, bunu gümüş mermilere şüpheyle yaklaşacak kadar uzun süredir yapan geliştiriciler için yazılmış yerel ilk web uygulamaları oluşturma hakkında düşündüğüm şey.
“Yerel - Önce” Aslında Ne Demektir (Ve Ölmeyecek Karışıklık)Bir şeyi açıklığa kavuşturmam gerekiyor çünkü bu konuşmayı buluşmalarda yapmaya devam ediyorum.Local - first, offline - first değildir."Bir servis çalışanı ekleyin ve paydos edin" değil. "PWA'nın eşanlamlısı değil. Bunların hepsinin konferans konuşmalarında bir araya geldiğini gördüm ve bu beni biraz çıldırtıyor.
Çevrimdışı ilk, uygulamanızın ağ kaybını zarif bir şekilde ele aldığı anlamına gelir, ancaksunucu hala gerçeğin kaynağıdır. Ağ geri geldiğinde sunucu kazanır. Önce önbellek (yanıtları önbelleğe alan hizmet çalışanları) bir performans optimizasyonudur. Eski verileri daha hızlı sunuyorsunuz, bu harika, ancak kimleri değiştirmediniz?SahippWA'lar bir dağıtım mekanizmasıdır: yüklenebilir, önbelleğe alınmış, anlık bildirimler. Bunların hiçbiri bir veri mimarisi değildir.
Local - first bir veri mimarisidir.Kullanıcınızın cihazı, verilerinin birincil kopyasını tutar. Uygulama yerel bir veritabanını okur ve yazar. Anında render alır. Arka plandaki sunucular veya diğer cihazlarla senkronize olur. Sunucu, mevcut olduğunda, bazı özel yetkilere (kimlik doğrulama, yedekleme, erişim kontrolü) sahip bir senkronizasyon eşidir. Ama kapı bekçisi değil.
Ink & Switch makalesi yedi ideal tanımladı ve bence hala geçerli. Ancak pratikte en önemli olan, her şeyi inşa etme şeklinizi değiştiren şey şudur:
İstemci, verileri göstermek için izin isteyen zayıf bir görünüm değildir. Danışan birdüğümkendi veritabanına sahip dağıtık bir sistemde.
Bu ayrım ince görünüyor. Değil. Tüm yığınını değiştirir.
Erken Dürüst Olun: Bunu Yapmamanız Gereken ZamanlarBunu zirveye yakın bir yere koyuyorum çünkü çok fazla geliştiricinin (bir zamanlar kendim de dahil olmak üzere) yeni bir mimari için heyecanlandığını ve ait olmadığı projelere yönlendirdiğini izledim. Önceki bir işte dahili bir analitik gösterge panosu için yerel bir ilk yaklaşımın işe yaraması için yaklaşık altı haftamı harcadım. Meslektaşım Sarah sonunda beni kenara çekti ve şöyle dedi:“Veriler sunucuda üretilir. Müşteriye kopyalanacak bir şey yok. Ne yapıyorsun ?"Haklıydı.
Verileriniz öncelikle sunucu tarafından oluşturulduğunda, Local - first kötü bir seçimdir. Analiz panoları, sosyal medya akışları, arama sonuçları: sunucuÜrünlerbu veriler, bu nedenle bunları API istekleri aracılığıyla tüketen istemci tamamen iyidir.
Güçlü işlem tutarlılığı gerektiren sistemler için yanlıştır. Bankacılık, ödeme işlemleri ve envanter yönetimi. İki kişi stoktaki son ürünü satın almaya çalışırsa, bu kararı veren tek bir yetkili veritabanına ihtiyacınız vardır.ACIDgaranti eder. Sonunda tutarlılık size para veya daha kötüsünü kaybettirir.
Çevrimdışı veya işbirliği ihtiyacı olmayan basit CRUD uygulamaları için aşırıya kaçıyor. İyi internete sahip bir ofiste beş kişi tarafından kullanılan dahili bir yönetici paneli oluşturuyorsanız, bir senkronizasyon motoru eklemek aşırı mühendislik gerektirir. Ve istemci cihazlara sığmayan büyük veri kümeleri için fiziksel olarak pratik değildir.
Ama işte burada parlıyor: not alma, belge düzenleme, işbirlikçi tasarım araçları, proje yönetimi, güvenilmez bağlantıya sahip saha uygulamaları, temel olarakveri gizliliği bir satış noktasıdır, yanı sıra herhangi bir şey ileGerçek zamanlı ekip işbirliğiBaşka bir deyişle,kullanıcı tarafından oluşturulan verilerbu, anlık etkileşimden yararlanır ve sunucu çöktüğünde hayatta kalmalıdır.
Birinin bana daha önce söylemesini istediğim bir şey daha var: Her şeyi yapmak zorunda değilsin. Şunun için local - first kullanarak en iyi sonuçları aldım:Spesifik Özellikler:aksi takdirde geleneksel uygulamalar içinde. Bir blog düzenleyicisinde çevrimdışı taslaklar. Standart REST olan bir proje yönetim aracının içindeki gerçek zamanlı işbirlikçi notlar.
"Önce yerel yelpazesi" gerçek bir şeydir ve bir özellikten başlamak, herkesin başlamasını önereceğim şeydir.
Kopyalar, İstekler DeğilGit kullandıysanız, zihinsel modeli zaten anlamışsınızdır.
SVN (hatırla SVN?) santralize edildi. Bir sunucu. Dosyaları kontrol eder, değişiklikler yapar ve sunucuya işlem yaparsınız. Sunucu kapalı mı? İşlem yapılamıyor. Tarihi bile göremiyorum.
Git, her geliştiriciye tam bir klon verdi. Yerel olarak işlem yaparsınız, yerel olarak şubeleşirsiniz ve yerel olarak birleşirsiniz. Hazır olduğunuzda itin ve çekin. Uzak veri havuzu önemlidir, ancak gerçeğin tek kopyası değildir.
Local - first web geliştirme, uygulama verileri için Git'tir.Her istemci cihaz, ilgili verilerin bir kopyasını (tam veya kısmi) tutar. Yazmalar yerel olarak gerçekleşir. Senkronizasyon arka planda itme/çekmedir. Çatışmalar, tanımlanmış birleştirme stratejileri aracılığıyla çözülür.
Pratikte bunun benim için ilk tıkladığı zamanı hatırlıyorum. Bir görev panosunun prototipini yapıyordum ve bir görev eklemek için bir işlev yazdım. Eski mimarimizde şöyle olurdu:
- API'ye GÖNDER.
- Yanıtı bekleyin.
- Başarılı olursa, yerel durumu güncelleyin.
- Başarısız olursa, hata tostunu gösterin ve belki de iyimser güncellemeyi geri alın.
Yerel ilk sürümde: yerel SQLite'a yazıldı, tamamlandı. Kullanıcı arayüzü aynı yerel veritabanından okunduğu için anında güncellendi. Senkronizasyon ne zaman olursa olsun gerçekleşti. Yükleme durumu yok, yazmanın kendisi için hata işleme yok, iyimser güncelleme mantığı yok (çünkü "iyimser" olacak hiçbir şey yok; yerel yazmaisDevlet
Sonuçlar her yerde dalgalanıyor. Veri getirmek için React Query veya SWR'ye ihtiyacınız yoktur, çünkü getirmiyorsunuz. Sunucu kaynaklı durum için Redux veya Zustand'a ihtiyacınız yoktur, çünkü yerel veritabanıisdurumunuz. Yönlendirmeniz API çağrılarını tetiklemez. Kimlik doğrulama farklı çalışır çünkü sunucu her okumada izinleri kontrol etmez.
Mekansal düşünen biriyseniz (benim gibi) yardımcı olabilecek görsel bir karşılaştırma:

Solda, her kullanıcı etkileşimi bir gidiş - dönüş yolculuğudur. Tıkla, bekle, oluştur. Sağda, okur ve yazar yerel veritabanına doğrudan çarpar. Senkronizasyon sunucusu hala orada, ancak işini arka planda yapıyor. Kullanıcı bunu asla beklemez. Temel değişim budur.
Ama ben kendimi aşıyorum. Senkronizasyon ve çatışmalar hakkında konuşmadan önce, danışanda verilerin gerçekte nerede yaşadığı hakkında konuşmamız gerekir.
Verilerin Müşteride Yaşadığı YerSıfırlalocalStorage. Senkronize (ana iş parçacığını engeller), 5 -10 MB boyutunda kapaklar ve yalnızca dizeleri depolar. Tema tercihi için uygun. Bu bir veritabanı değil.
IndexedDB kimsenin sevmediği bir beygirdir. Her tarayıcıda bulunur, asenkrondur, yüzlerce megabaytı işleyebilir ve API'si ile çalışmak kesinlikle sefildir. Doğrudan toplamda bir kez kullandım. Şimdi bunu soyutlamalar yoluyla kullanıyorum ya da daha sık olarak hiç kullanmıyorum.
Çünkü 2026 'daki asıl hikaye WebAssembly üzerinden tarayıcıda çalışan SQLite.
Kulağa parti numarası gibi geldiğini biliyorum ama değil. WASM'ye derlenen, Origin Özel Dosya Sistemine (OPFS) devam eden SQLite, sizeilişkisel veritabanıtarayıcıda. Tam SQL sorguları. İşlemler. Dizinler. İşler.
OPFS, bunu pratik hale getiren yeni API'dir. Web uygulamalarına, SQLite'ın tam olarak ihtiyaç duyduğu yüksek performanslı senkronize erişime (Web Çalışanlarında) sahip korumalı bir dosya sistemi sağlar. OPFS'den önce, SQLite'ı bellekte çalıştırabilir ve işe yarayan ancak yavaş ve kırılgan olan IndexedDB'ye manuel olarak devam edebilirsiniz.
İşte gerçek bir projede başlatmanın kabaca nasıl göründüğü (wa - sqliteburada, en iyi şansa sahip olduğum kütüphane):
import { SQLiteAPI } from 'wa - sqlite ';
import { OPFSCoopSyncVFS } from 'wa - sqlite/src/examples/OPFSCoopSyncVFS.js ';
async fonksiyonuinitDatabase () {
const module = await SQLiteAPI.initialize ();
const vfs = new OPFSCoopSyncVFS(' pm - tool - db ');
vfs.initialize (modül) bekleyin;
const db = modülü bekle.open_v2 (' workspace.db ');
// HACK: wa - sqlite Safari'de eşzamanlı yazmaları iyi işlemiyor,
// böylece bir kuyrukta serileştiririz. Bkz. vlcn - io/wa - sqlite #247
wait module.exec(db,PRAGMA journal_mode=WAL);
wait module.exec(db,MEVCUT DEĞİLSE TABLO OLUŞTURMA görevleri (
kimlik METNİ BİRİNCİL ANAHTARI,
başlık METNİ NULL DEĞİL,
durum METNİ VARSAYILAN 'iş listesi ',
atanan_kimlik METNİ,
proje_kimlik METNİ BOŞ DEĞİL,
pozisyon GERÇEK TEMERRÜT 0,
mETİN VARSAYILANI OLARAK _ oluşturuldu (tarih saat (' şimdi ')),
_ METİN VARSAYILANI OLARAK güncellendi (tarih saat (' şimdi
'))));
return db;}
Üretimde, tüm veritabanı erişimini, mutasyonları seri hale getiren bir yazma kuyruğuna sarıyorum. Ayrıca her başarısız yazıyı Sentry'ye tam SQL ifadesiyle (açıkçası PII temizlendi) günlüğe kaydediyorum çünkü bir kullanıcının tarayıcısında hata ayıklama veritabanı sorunları bu telemetri olmadan cehennemdir.
Neredeyse iki günümü boşa harcadığım bir gotcha: Safari'nin OPFS uygulaması, Chrome'unkinden ince şekillerde farklı davranıyor. Özellikle, bir hataya çarptım.createSyncAccessHandle()safari 18 'deki belirli iframe bağlamlarında sessizce başarısız olur. Hata yok, istisna yok. İşe yaramıyor işte. Safari'de daha yavaş ama en azından işlevsel olan IndexedDB destekli kalıcılığa geri döndüm. (Bana söylendiSafari 19/26bunu düzeltir, ancak henüz doğrulamadım.)
Kullandığım seçeneklerin hızlı bir şekilde karşılaştırılması:
| Depolama | Aşağıdakileri yapmaya yarar: | TRAFİK İŞARETLERİNE |
|---|---|---|
| IndexedDB | Geniş uyumluluk, orta düzeyde veri | Korkunç DX, SQL yok, ayrıntılı |
| OPFS + SQLite WASM | İlişkisel veriler, karmaşık sorgular, ciddi uygulamalar | Safari tuhaflıkları, ~400KB paket ekleme |
| PGlite (WASM'de Postgres) | İstemcide tam Postgres uyumluluğu | Daha yeni, daha büyük sepet, hala olgunlaşıyor |
Ben de denedimcr - sqlite, doğrudan SQLite tablolarına CRDT sütun desteği ekler. Zekice bir fikir, ancak 2025 'in sonlarında değerlendirdiğimde üretim kullanımı için çok erken olduğunu gördüm. Birleştirme anlambilimi bazen şaşırtıcıydı ve SQLite içinde CRDT durumunu ayıklamak acı vericiydi. Bu yılın ilerleyen zamanlarında tekrar ziyaret ederdim.
Verilerin yerel olarak depolanması çözülmüş bir sorundur. Cihazlar ve kullanıcılar arasında güvenilir bir şekilde senkronize etmek, gri saçlarınızı kazandığınız yerdir.
Birden fazla kopya bağımsız olarak okuyup yazabildiğinde, değişiklikleri uzlaştırmak için bir mekanizmaya ihtiyacınız vardır. Temelde dört yaklaşım var ve bunlardan üçünü kullandım.
CRDT'ler (Çatışmasız Çoğaltılmış Veri Türleri)eşzamanlı düzenlemelerin her zaman çakışma olmadan birleştirilebileceği ve matematiksel olarak garanti edilebileceği şekilde tasarlanmış veri yapılarıdır. Yjs, JavaScript'teki en popüler uygulamadır ve gerçek zamanlı işbirliğine dayalı metin düzenleme için gerçekten mükemmeldir. Son şirketimde işbirlikçi bir belge editörü oluşturmak için kullandım ve deneyim çoğunlukla iyiydi, ancak çatışma çözme bölümündeki sorunlu noktalara gireceğim.
Paylaşılan bir Yjs belgesini oluşturmak pratikte şöyle görünür:
import * as Y from 'yjs ';
'y - web soketinden' {WebsocketProvider} içe aktar;
const ydoc = new Y.Doc ();
const provider = yeni WebsocketProvider(
'wss :// sync.our-app.dev ',
'workspace - a1b2c3d4 ',
ydoc
);
const görevler = ydoc.getMap (' görevler ');
// Görev ekle
const task = new Y.Map ();
task.set(' title ', '3. Çeyrek yol haritası taslağını inceleyin ');
task.set(' tamamlandı ', yanlış);
task.set(' atanan ', 'maria ');
// TODO: bunu bir kez düzgün yazın; yjs daha iyi TS türleri ihraç eder
// iç içe haritalar için. Şimdilik, bu iyi çalışıyor. tasks.set (' f47ac10b -58cc -4372 - a567 -0e02b2c3d479 ', herhangi bir görev);
tasks.observeDeep(() => {//
Kullanıcı Arayüzünü Yeniden Oluştur. Uygulamada, bunu ~ 16ms'ye çıkarıyorum
// çünkü observeDeep aktif işbirliği sırasında ÇOK FAZLA yangın çıkarır
renderTaskList(tasks.toJSON ();});
Automergerust tarafından desteklenen ve belge odaklı bir modele sahip diğer büyük CRDT kütüphanesidir. Daha az kullandım ama yemin eden ekipler tanıyorum.Lorodaha yeni, Pas tabanlı ve daha iyi performans iddia ediyor. Loro ile henüz bir şey göndermedim.
veritabanı çoğaltmasıdiğer büyük yaklaşımdır ve dürüst olmak gerekirse, Google Dokümanlar tarzı gerçek zamanlı metin düzenlemeye ihtiyaç duymayan çoğu uygulama için daha iyi bir seçim olduğunu düşünüyorum. Fikir basittir: sıhhi tesisatı yöneten bir senkronizasyon motoru ile bir sunucu veritabanı (Postgres) ve bir istemci veritabanı (SQLite) arasındaki satırları çoğaltın.
PowerSyncbunu iyi yapar. Mutasyonlar için bir geri yazma yolu ile Postgres'ten istemci SQLite'a tek yönlü replikasyon sağlar. ElectricSQL daha iddialıdır ve Postgres ile SQLite arasında tam aktif - aktif senkronizasyon sağlar. PowerSync'i üretimde kullandım veElectricSQLprototiplerde. PowerSync, 2026 'nın başlarında ikisini de değerlendirdiğimde daha kararlı hissetti, ancak yürütmeyi başarırlarsa ElectricSQL'in yaklaşımı daha güçlü.
Triplittamamen farklı bir açı alır: yerleşik senkronizasyona sahip tam yığın bir veritabanıdır, bu nedenle "istemci DB" ve "sunucu DB" yi ayrı ayrı düşünmezsiniz. Bir hafta sonu prototipinin ötesinde denemedim, ancak geliştirici deneyimi şaşırtıcı derecede güzeldi.
Olay kaynaklama (event sourcing) kullanılır.(mevcut durumdan ziyade bir mutasyon günlüğünün senkronize edilmesi) yaklaşımdırLiveStorealır. Entelektüel olarak çekici ve zaman zaman yararlı buluyorum, ancak pratikte, bir olay günlüğünden durumu yeniden oluşturmanın çoğu uygulamanın ihtiyaç duymadığı karmaşıklığı eklediğini buldum. Tartışmalı görüşüm: Etkinlik kaynak kullanımı, uygulama geliştirme için aşırı tavsiye edilmektedir. Denetim günlükleri ve belirli alanlar için harika, ancak bir görev panosu için mi? Sadece satırları senkronize edin.
Herkes buna katılmayacak. Etkinlik kaynak kullanımının tutkulu savunucuları olduğunu biliyorum ve konferanslarda bu konuda en az iki kez yanıldığım söylendi. Belki de henüz bunun için doğru uygulamayı oluşturmadım.
Çatışmalar: Herkesin Korktuğu ŞeyÇatışma çözümünün korkutucu ve çözülemez bir sorun olduğunu düşünürdüm. Bunu işleyen üç uygulama oluşturduktan sonra, bunu şu şekilde revize ederdim:yönetilebilirbelirli veri modeliniz hakkında dikkatlice düşünmenizi gerektiren bir sorundur ve çoğu geliştirici bunu aşırı düşünmektedir.
Çatışmalar, iki kopya birbirlerinin değişikliklerini görmeden aynı verileri değiştirdiğinde meydana gelir. Kullanıcı A, çevrimdışıyken telefonundaki bir görev başlığını düzenler. B kullanıcısı aynı başlığı dizüstü bilgisayarında düzenler. İkisi de tekrar çevrimiçi oluyor. Şimdi ne olacak?
Bunu ele almaya yönelik ilk girişimim utanç verici derecede safçaydı:
// İlk denemem. Bunu yapma.
functionresolveConflict (local:any, remote:any) {
// sadece... uzaktan kumandayı mı alayım? Emin misin?
uzaktan kumandayı döndür;}
Sorun açıktır: yerel değişiklikler sessizce bırakılır. A Kullanıcısı bir başlığı düzenler, eşitler ve düzenlemeleri kaybolur. Bunun olduğunu bile bilmiyorlar.
Çoğu durumda gerçekten işe yarayan şey,son yazma - kazançları (LWW)éaraziseviye, kayıt seviyesi değil. A Kullanıcısı başlığı ve B Kullanıcısı son tarihi değiştirirse, farklı alanlara dokundukları için her iki değişikliği de saklarsınız. Yalnızca her ikisi de aynı alanı değiştirdiğinde gerçek bir çakışma yaşarsınız ve daha sonra daha sonraki zaman damgasını seçersiniz.
interface FieldValue {
value: string | number | boole;
// Çoğu bağı kırmak için yeterli hassasiyete sahip ISO zaman damgası
updatedAt: string;
// Zaman damgaları eşleştiğinde eşitliği bozan Müşteri Kimliği. // Bu düşündüğünden daha sık olur. clientId:string;}
fonksiyon pickWinner
(a: FieldValue, b: FieldValue): FieldValue {
const timeA = new Date(a.updatedAt ).getTime ();
const timeB = new Date(b.updatedAt ).getTime ();
if (timeA!== timeB) return timeA > timeB? a:b;
// Zaman damgaları eşleştiğinde deterministik eşitlik bozucu
return a.clientId > b.clientId? a:b;}
// Uygulamada, bunu alan başına tüm kayıt boyunca uyguluyorum. fonksiyon mergeTask(local: Record<string, FieldValue >, remote: Record<string, FieldValue >) {
const birleştirildi: Record<string, FieldValue >= {};
const allKeys = new Set ([...Object.keys(local), ...Object.keys(remote)]);
for (tüm tuşların const tuşu) {
if (!local[key]) { birleştirilmiş[key] = uzak[key]; devam et;}
if (!remote[key]) { birleştirilmiş[key] = yerel[key]; devam et;}
birleştirilmiş[key] = pickWinner (yerel[key], uzak[key]);
}
birleştirilmiş iade;
}
Üretim uygulamamızda, bu, kullanıcı tarafından görülebilen herhangi bir sorun olmadan çatışmaların yaklaşık % 95 'ini ele alır. Kalan durumlar için (aynı metin alanını düzenleyen iki kişi), LWW bir kişinin düzenlemesinin sessizce kazandığı anlamına gelir. Bir görev başlığı için mi? Dürüst olmak gerekirse, bu genellikle iyidir. Belge gövdesi için mi? Hayır. CRDT'lerin geçimini sağladığı yer burasıdır.
Ama vurana kadar takdir etmediğim daha ince bir sorun var:anlam çatışmaları. Veriler yapısal düzeyde temiz bir şekilde birleşir, ancak sonuç anlamsızdır. Her ikisi de çevrimdışı olan iki kullanıcı, farklı toplantılarla aynı 2 PM toplantı zamanını rezerve eder. Alan düzeyinde birleştirme, farklı kayıtlara yazdıkları için her iki yazıyı da kabul eder. Yapısal bir çatışma yok. Ancak çifte rezervasyonunuz var ve birleştirme işlevinizin bunun bir sorun olduğu konusunda hiçbir fikri yok.
Anlamsal çakışmalar, uygulama düzeyinde doğrulama gerektirir ve bunun senkronizasyon sırasında sunucuda gerçekleşmesi gerekir. Senkronizasyon motorunuz verileri yapısal olarak birleştirir, ancakbaşvurunuzun gönderime hazır olacağına inanıyoruz.sunucunun sonucu kabul etmeden önce etki alanı değişmezlerini kontrol etmesi gerekir. Üzerine düştüğüm yaklaşım (iki kez yanlış yaptıktan sonra): geri yazma aşamasında sunucuda doğrulayın, ancakİşaretlesessizce reddetmek yerine ihlalleri reddeder.
Demek istediğim şu. İstemci senkronizasyon sırasında mutasyonları sunucuya ittiğinde, sunucu bunları Postgres'e uygulamadan önce bir kısıtlama doğrulama katmanı aracılığıyla çalıştırır:
interface SyncViolation {
type :' scheduling_conflict '|' capacity_exceeded '|' stale_assignment ';
recordId: string;
description: string;
// Müşterinin bağlamı gösterebilmesi için çakışan kayıtlar
çakışanKayıtlar: string[];
// Bu ihlal ne zaman tespit edildi
detectedAt:string;}
eşzamansız işlev validateSyncBatch(
mutasyonlar:SyncMutation [], serverDb:
Veritabanı
): Söz<{accepted:SyncMutation []; ihlaller: SyncViolation []}>{
const kabul edildi: SyncMutation [] = [];
const ihlalleri: SyncViolation [] = [];
for (const mutation of mutations) {
if (mutation.table ===' calendar_events ') {
// Çifte rezervasyonu kontrol edin
const overlapping = wait serverDb.query(TAKVİMDEN kimlik ve başlık SEÇİN _etkinlikler
WHERE room_ id =? VE id !=?
VE start_time <? VE bitiş_ zamanı >?,[ mutation.data.room_id,
mutation.data.id,
mutation.data.end_time, mutation.data.start_time]);
if (overlapping.length > 0) {
violations.push({
type :' schedule_conflict ',
recordId:mutation.data.id,
açıklama:"${overlapping[0 ].title }" ile çakışıyor,
conflicttingRecords:overlapping.map (r => r.id),
detectedAt: new Date
().toISOString ()});
// Yazmayı yine de kabul edin, ancak işaretleyin
// Alternatif onu reddetmektir, ancak daha sonra kullanıcının
// yerel durum ve sunucu durumu farklılaşıyor ve bu daha da kötü
accepted.push(mutation);
continue
;}}
accepted.push(mutation);
}
iade { kabul edildi, ihlaller };
}
Buradaki kilit karar — ve bu konuda ileri geri gittim — bizkabul etdoğrudan reddetmek yerine, çelişkili yaz ve işaretle. Reddederseniz, kullanıcının yerel veritabanında sunucunun kabul etmeyi reddettiği bir kayıt vardır ve şimdi kurtarılması gerçekten zor bir durum sapması durumundasınız demektir. Önce reddetme yaklaşımını denedim ve istemcide kullanıcıların sunucuda olmadığı için silemediği hayalet kayıtlara yol açtı. Kabus.
Bunun yerine, sunucu yazmayı kabul eder, ihlali saklar ve ihlali istemciyle senkronize eder. İstemci engelleyici olmayan bir bildirim gösterir:"' 3. Çeyrek Planlaması ‘toplantınız, 14.00‘ te B Odasındaki ’Tasarım İncelemesi’ ile çakışıyor. Çözmek için dokunun ."Kullanıcı dokunur, her iki toplantıyı da görür ve yeniden planlamak veya iptal etmek için birini seçer. Çözünürlük, geri senkronize olan normal bir yazmadır.
Bu mükemmel mi? Hayır. İhlalin ne zaman oluşturulduğu ile kullanıcının ne zaman çözdüğü arasında, her iki çakışan kaydın da bulunduğu bir pencere vardır. Toplantı odaları için bu tolere edilebilir. İki kişinin son ürünü "satın aldığı" envanter yönetimi gibi bir şey için, bu pencere kabul edilemez ve tam da bu yüzden daha önce güçlü işlem tutarlılığı gerektiren sistemler için local - first'in yanlış olduğunu söyledim.
Hala bu örüntüyü yineliyorum. Kullanıcılar bildirimleri görmezden gelirse ihlal tablosu büyür (bildirimlerin süresi 72 saat sonra sona erer, bu da keyfi hissettirir).Kisunucuda doğrulanacak değişmezler, esasen istemci tarafı uygulama mantığınızın dışında bir dizi paralel iş kuralını korumanızı gerektirir. Zarif değil. Ama işe yarıyor ve geliştirdiğim uygulama sınıfı için bulduğum en iyi yaklaşım bu. Daha temiz bir şey inşa ettiyseniz, bunu gerçekten duymak istiyorum.
Yjs gibi CRDT'ler için, karakter düzeyinde (metin için) çatışma çözümü oldukça iyi çalışır. Aynı paragrafta yazan iki kişi, her iki karakter kümesinin de mantıklı bir sırayla göründüğünü görecektir. Ancak yapılandırılmış verilerin (haritalar, diziler, iç içe geçmiş nesneler) CRDT ile birleştirilmesi sizi şaşırtan sonuçlar üretebilir. Bir keresinde, bir birleştirmeden sonra Yjs destekli bir görev listesinin yinelenen öğelerini izledim, çünkü iki kullanıcı aynı listeyi çevrimdışı olarak yeniden sıraladı ve CRDT'nin listesi birleştirme anlambilimi sıralarını birleştirdi. Teknik olarak doğru. Pratik olarak kafa karıştırıcı. Sonunda, bir hack gibi hissettiren ancak sorunu çözen birleşme sonrası tekrardan arındırma adımı ekledik.
Kullanıcıyla Git tarzı çakışmaları ne zaman ortaya çıkarmalısınız? Deneyimlerime göre, tipik uygulama verileri için neredeyse hiçbir zaman. Kullanıcılar birleştirme çakışmalarını çözmek istemiyor. Uygulamanın bunu anlamasını istiyorlar. İstisna, yüksek riskli içeriktir: yasal belgeler, tıbbi kayıtlar, bir düzenlemeyi sessizce bırakmanın gerçek zarara neden olabileceği her şey.
Şu Anki AraçlarSize 26 'nın ortalarından itibaren mevcut olan araçlar hakkında dürüst okumalarımı sunacağım, bu alanın yeterince hızlı hareket ettiği ve siz okuduğunuzda bunların bir kısmının modası geçmiş olabileceği uyarısında bulunacağım.
Yjsen olgun CRDT kütüphanesidir. Üretime hazır, büyük topluluk, çoğu işbirlikçi editörle (TipTap, BlockNote, Lexical) entegre olur. Gerçek zamanlı işbirliğine dayalı düzenlemeye ihtiyacınız varsa, buradan başlayın.
Automergesağlam, Pas destekli ve Yjs'den daha belge odaklı bir yaklaşım benimsiyor. Veri modelinin bir belge metaforuna uyduğu uygulamalarda iyi kullanıldığını gördüm. Yjs'den daha az entegrasyon var, ancak çekirdek iyi tasarlanmış.
PowerSyncmevcut bir Postgres arka ucu olan ve çevrimdışı destek eklemek isteyen ekipler için önereceğim şeydir. Üretime hazırdır, dokümanlar iyidir ve zihinsel model (Postgres istemci SQLite ile senkronize olur, istemci yazıları tanımlanmış bir yükleme yolundan geçer) hakkında akıl yürütmek kolaydır. Uygulamamızda, yaklaşık 5.000 görev içeren bir çalışma alanı için ilk senkronizasyon, iyi bir bağlantıda yaklaşık 1,2 saniye ve azaltılmış bir 3G simülasyonunda yaklaşık 3,5 saniye sürer. Bu bizim için kabul edilebilirdi.
ElectricSQLdaha iddialı bir şey için gidiyor: Postgres ve SQLite arasında gerçek aktif - aktif çoğaltma, hangi verilerin hangi istemciyle senkronize olduğunu tanımlayan "şekiller" ile. Bunun başarılı olmasını istiyorum çünkü prototiplerdeki geliştirici deneyimi mükemmeldi. Ancak Şubat 2026 'da üretim için değerlendirdiğimde, bunun yerine PowerSync ile gittiğim yeterince pürüzlü kenara (özellikle şekil yönetimi ve yeniden bağlantı davranışı etrafında) çarptım. Tekrar ziyaret etmeyi planlıyorum.
Triplitbir hafta sonu prototipinde beni etkiledi. Yerleşik senkronizasyona sahip tam yığın veritabanı, NICE TypeScript API. Gerçek üretim yükü ile stres testi yapmadım ve yapmadan önce yapmak isterim.
Sıfır(Rocicorp'tan, Replicache kişileri) ilginçtir, çünkü satır çoğaltma modelinden farklı olan senkronizasyon için sorgu tabanlı bir yaklaşım gerektirir. Replicache, Zero lehine gün batımıydı, bu da size bu alanda yaklaşımların ne kadar hızlı geliştiği hakkında bir şeyler söylüyor. İzlemeye değer, ama bir prodüksiyon uygulaması için henüz geliştirmezdim.
TinyBaseküçük uygulamalar veya prototipleme için mükemmel olan hafif bir reaktif mağazadır. Kişisel bir yan proje (okuma takipçisi) için kullandım ve çok beğendim. Ekip ölçekli bir ürün için kullanacağımdan emin değilim.
PGlite(WASM'ye derlenen postgres) vahşidir. İstemci ve sunucuda aynı SQL lehçesi. ElectricSQL ile birlikte, teorik olarak her yerde aynı sorguları çalıştırabilirsiniz. Bence işler uzun vadede bu noktaya geliyor, ancak PGlite'ın paket boyutu ve bellek alanı hala mobil tarayıcılar için endişe kaynağı.
Replicache gün batımının bana öğrettiği bir şey var: Mimarinizi geri dönüş planı olmayan küçük bir şirketin tek bir aracına yatırmayın. Senkronizasyon katmanımı, motorları aylar değil, birkaç hafta içinde değiştirebilecek kadar soyut tutuyorum. Bunun zamansız bir soyutlama gibi göründüğünü biliyorum, ama bu kadar genç bir alanda, bence bu sadece ihtiyatlılık.
Gerçek Bir Uygulama Oluşturma: Mimari, Kimlik Doğrulama ve GeçişlerPratikte yerel bir ilk uygulamayı nasıl yapılandırdığımın üzerinden geçmek istiyorum, çünkü blog gönderilerinde gördüğünüz katman diyagramları nadiren kodun neye benzediğiyle eşleşiyor.
İşbirlikçi bir proje yönetim aracı için mevcut yığınım şu şekildedir:
- UIAsla çağırmayan bileşenlere tepki verin
getirmeveri okumaları için. - Sorgu katmanı:
useLiveQueryyerel SQLite veritabanına abone olan ve veriler değiştiğinde otomatik olarak yeniden işleyen kancalar. - 本地数据库SQLite via wa - sqlite, OPFS'ye devam etti.
- Mutasyon katmanı:Düz
INSERT/GÜNCELLEME/SİLyerel SQLite'a karşı ifadeler. - SENKRONİZASYON:PowerSync, yerel SQLite ile Postgres arka uç arasındaki kopyalamayı yönetir.
- Sunucu:Postgres, bir Node.js kimlik doğrulama hizmeti ve küçük bir senkronizasyon doğrulama katmanı.
Bileşen kodu, yazdıklarıma kıyasla neredeyse saçma bir şekilde basit görünüyor:
import { useLiveQuery} from '@powersync/react ';
import { db } from '../lib/database ';
function TaskBoard ({ projectId }:{ projectId:string }){
const görevler = useLiveQuery(Projenin _ id = OLDUĞU GÖREVLERDEN * ÖĞESİNİ SEÇİN? VE arşivlendi = pozisyona GÖRE 0 SİPARİŞ,
[projectId
]);
asenkron fonksiyonaddTask (title:string) {
db.execute bekle(Görevlere EKLE (id, title, project_id, position, created_at)
VALUES (?,?,?,?, datetime(' now ')),[ crypto.randomUUID(), title, projectId, tasks.length]);
// Hepsi bu kadar. useLiveQuery değişikliği otomatik olarak alır.
// Geçersiz kılma yok, yeniden getirme yok, yükleme durumu yok.}
// Yükleme kontrolü yok. Veriler yerel. İlk senkronizasyondan sonra her zaman oradadır.
return (<
div>
{task.map (task => < TaskCard key={ task.id} task ={ task }/>)}
< NewTaskInput onSubmit={ addTask }/>
</ div
>);}
Bunu, kodun en az iki katı olan ve yükleme durumlarını, hata durumlarını, geri alma ile iyimser güncelleme mantığını ve önbellek geçersiz kılmayı içeren React Query + REST eşdeğeriyle karşılaştırın. Özlemiyorum.
Yerel - İlk Dünyada Auth
Kimlik doğrulama kabaca geleneksel uygulamalarla aynı şekilde çalışır: JWT tokenleri, OAuth akışları ve oturum yönetimi. Belirteç, her bir istek yerine senkronizasyon bağlantısını doğrular. Veriler zaten yerel olduğu için çevrimdışı erişim çalışır. Veriler başlangıçta senkronize edildiğinde kullanıcının kimliği doğrulandı.
Yetkilendirme daha zordur ve bence yerel ilk makalelerin çoğu bunu yeterince açıklamıyor. Tüm veritabanınızı her istemciyle senkronize edemez ve yetkisiz verileri gizlemek için istemci tarafı koduna güvenemezsiniz. Birisi DevTools'u açacak, yerel SQLite dosyasını bulacak ve her şeyi görecektir. Müşteri bir güven sınırı değildir.
Senkronizasyon katmanında yetkilendirme uygularsınız. PowerSync, hangi satırların hangi istemcilere gideceğini tanımlayan "senkronizasyon kurallarına" sahiptir. ElectricSQL'in "şekilleri" vardır. Her iki durumda da, sunucu yalnızca kullanıcının görmeye yetkili olduğu verileri gönderir. İstemci yanıt gönderdiğinde, sunucu bunları Postgres'e uygulamadan önce yetkilendirme kurallarına göre doğrular. Bir kullanıcı değiştirmemesi gereken bir şeyi değiştirmeye çalışırsa, sunucu senkronizasyon sırasında bunu reddeder.
Ayrıca şunu da belirtmek istiyorumUçtan Uca Şifreleme, çünkü önce yerel ile doğal olarak eşleşir. Veriler istemcide yaşadığından, senkronizasyondan önce şifreleyebilirsiniz. Sunucu, okuyamadığı şifreli blobları depolar ve aktarır. Anytype gibi uygulamalar bunu yapar. Mevcut uygulamamızda E2EE'yi uygulamadık, ancak daha hassas verileri ele aldığımız zamanlar için yol haritasındayız.
Bin Cihazda Şema Geçişi
Bu beni ilk kez hazırlıksız yakaladı. Sunucuda, kontrol ettiğiniz bir veritabanına karşı bir geçiş çalıştırırsınız. İstemcide, her kullanıcının uygulamayı en son ne zaman açtıklarına bağlı olarak şemanızın herhangi bir sürümünü çalıştırabilecek kendi veritabanı vardır.
Uygulama başlangıcında bir sürüm numarasını kontrol eden basit bir geçiş çalıştırıcısı kullanıyorum:
const MIGRATIONS
=[{
sürüm: 1,
sql:MEVCUT DEĞİLSE TABLO OLUŞTURMA görevleri (
kimlik METNİ BİRİNCİL ANAHTARI,
başlık METNİ NULL DEĞİL,
durum METNİ VARSAYILAN 'iş listesi ',
proje_kimlik METNİ BOŞ DEĞİL,
mETİN VARSAYILANI OLARAK _ oluşturuldu (tarih saat (' şimdi
')));},
{
sürüm: 2,
// Sprint 4 'te öncelik ve due_date eklendi
sql:TABLO GÖREVLERİNİ DEĞİŞTİR SÜTUN önceliği TAMSAYI EKLE VARSAYILAN 0;
TABLO GÖREVLERİNİ DEĞİŞTİR SÜTUN bitiş tarihi EKLE _tarih METNİ;},
{
sürüm: 3,
// Çevrimdışı görüntüleme için normalize edilmiş atanan adı.
// Evet, bunun bir değiş tokuş olduğunu biliyorum. BİRLEŞME ÖLDÜRÜYORDU
// düşük kaliteli Android cihazlarda performans.
sql:TABLO GÖREVLERİNİ DEĞİŞTİR SÜTUN ATANANINIEKLE_ ad METNİ VARSAYILANI '';}
];
asenkron fonksiyon runMigrations(db: Veritabanı) {
db.execute bekle(MEVCUT DEĞİLSE TABLO OLUŞTUR _ şema_sürüm (sürüm TAMSAYI));
const rows = wait DB.EXECUTE (' SELECT version FROM_SCHEMA_VERSION ');
const currentVersion = rows.length > 0 ? rows[0 ].version : 0;
for (const migration of MIGRATIONS) {
if (migration.version > currentVersion) {
console.log(Yerel veritabanı v${migration.version} sürümüne taşınıyor);
waitdb.execute (' BEGIN ');
tRY {
wait db.execute(migration.sql);
db.execute bekle(
'schema_version (rowid, version) DEĞERLERİNİ (1, ?) GİRİN veya DEĞİŞTİRİN ',
[migration.version
]);
waitdb.execute (' COMMIT ');
} catch (err ){
db.execute (' GERİ ALMA ') bekleyin;
// Üretimde, bu,
// aktarım sürümü ve hata ayrıntıları
throw err
;}}}
Taşıma işlemlerinizi katkı sağlayacak şekilde tasarlayın.Varsayılanlı yeni sütunlar. Yeni tablolar. Kesinlikle zorunlu olmadıkça sütunları yeniden adlandırmayın veya bırakmayın, çünkü eski uygulama sürümlerini çalıştıran kullanıcılar verileri senkronize etmeye devam edecektir ve sunucunuzun uyumsuzluğu ele alması gerekir. Bunu, eski bir müşterinin hala yazmakta olduğu ve bir hafta sonu boyunca yaklaşık 200 kullanıcı için sessiz senkronizasyon hatalarına neden olan bir sütunu düşürdüğümde zor yoldan öğrendim. Eğlenceli değil.
Bugün Yeni Bir Projeye Başlıyor OlsaydımBu bana çok soruluyor, bu yüzden şu anki cevabım şu. Her altı ayda bir değişir.
Gerçek zamanlı özelliklere ve çevrimdışı desteğe sahip ortak çalışmaya dayalı bir uygulama için şununla başlarım:Tepki Gösterön uçta,PowerSyncsenkronizasyon için,Wa - sqlite aracılığıyla SQLiteistemci üzerinde (Safari için IndexedDB yedeklemeli OPFS'ye devam etti) veSupabase(bu da bana Postgres, auth ve satır düzeyinde güvenliği kutudan çıkarır). KullanırdımYjscRDT'ler veri modelinize anlamlı bir karmaşıklık kattığından, yalnızca zengin metin işbirliğine ihtiyacım varsa ve bunu yapmazsam bundan kaçınırım.
Çoğunlukla çevrimdışı desteğe ve anında okumaya ihtiyaç duyduğum ancak işbirliğinin ikincil olduğu daha basit bir uygulama için, senkronizasyon motorunu tamamen atlayabilir ve sadece bir REST API'sini iten/çeken özel bir senkronizasyon katmanına sahip yerel bir SQLite veritabanı kullanabilirim. Bunun tekerleği yeniden icat etmek gibi geldiğini biliyorum, ancak basit durumlarda, tam olarak anladığınız özel bir senkronizasyon, ihtiyacınız olmayan kavramları ekleyen genel amaçlı bir senkronizasyon motorundan daha iyidir.
Şu anda üretim için ElectricSQL veya Zero'yu kullanmazdım, kötü oldukları için değil, aradığım bir şey için onlara güvenmeden önce 6 -12 ay daha olgunluk istediğim için. Daha önce erken aşama altyapı üzerine inşa edilerek yakıldım (erkenGök Taşıve şimdi yenilik riskini nerede kabul ettiğim konusunda daha temkinliyim.
Performans: Aslında Hızlı Olan ve Acıtan Nedir?Okumalar anlıktır. Bu pazarlama değildir. 500 görevden oluşan bir liste için yerel bir SQLite veritabanını sorgulamak, M2 MacBook'umda iki milisaniyeden az ve orta sınıf bir Android telefonda yaklaşık sekiz milisaniye sürüyor. Ağ yok. Dönücü yok. Yükleme durumu yok.
Yazılar da anlıktır.Görevlere EKLEyerel olarak çalışır, kullanıcı arayüzü reaktif olarak güncellenir ve senkronizasyon her zaman gerçekleşir. Kullanıcılar yazıları anlık olarak algılarlar çünkü öyledirler.
İlk senkronizasyon, maliyeti ödediğiniz yerdir. Yerel kopyayı ilk yükte (veya yeni bir cihazda) önyüklemek, potansiyel olarak megabaytlarca veri indirmek anlamına gelir. Uygulamamızda, 5.000 görev, 200 proje ve 50 kullanıcıya sahip bir çalışma alanı, geniş bantta yaklaşık 1,2 saniye ve yavaş bir mobil bağlantıda dört ila beş saniye sürer. Bunu kısmi senkronizasyonla (yalnızca kullanıcının aktif projelerini senkronize ederek) ve ilk senkronizasyon sırasında tek seferlik bir "Çalışma alanınızı ayarlama" ekranı göstererek azaltıyoruz. Bu ilk senkronizasyondan sonra, artımlı güncellemeler küçüktür.
Paket boyutu gerçek bir endişe kaynağıdır. WASM'de derlenen SQLite, JavaScript paketinize yaklaşık 400 KB gzip ekliyor. Bu önemsiz değil, özellikle de mobil cihazlarda Etkileşim Zamanı'nı önemsiyorsanız. Veritabanı modülünü dinamik olarak tembelce yüklerimalmaböylece ilk render'ı engellemez.
Hafıza diğer yakalayıcıdır. SQLite WASM bellekte çalışır ve agresif bellek sınırlarına sahip mobil tarayıcılarda, büyük bir veritabanı sekme çökmelerine neden olabilir. Senkronize edilmiş veri kümesini kısmi senkronizasyon yoluyla küçük tutmanın ve eski verileri budama konusunda agresif olmanın ötesinde bunun için harika bir çözüm bulamadım.
Not: Hafıza sorunlarından bahsetmişken, okuyordumVeri Yoğun Uygulamaların Tasarlanmasımartin Kleppmann tarafından üçüncü kez yazıldı. Her yeniden okuduğumda yeni bir şey yakalıyorum. Okumadıysanız ve dağıtılmış verileri düşünüyorsanız, önce durun ve okuyun.
Bu Şeyi Test EtmeBunu kısa tutacağım çünkü dürüst cevap, yerel öncelikli uygulamaları test etmenin geleneksel uygulamaları test etmekten daha zor olduğu ve araçların henüz mükemmel olmadığıdır.
Benim için işe yarayan şey: birleştirme mantığı için birim testleri (bunlar saf işlevlerdir, test edilmesi kolaydır), bellekteki iki istemci örneğini döndüren ve eşzamanlı düzenlemelerden sonra bunların birleştiğini doğrulayan entegrasyon testleri ve Playwright E2E testlericontext.setOffline(true)çevrimdışı/çevrimiçi geçişleri simüle etmek için.
İyi çözemediğim şey: yalnızca çatışma çözümü sırasında meydana gelen hataların belirli bir zamanlamayla yeniden üretilmesi. Bir kullanıcı bir görevin olduğunu bildirdiğinde“açıklamasını kaybettim”Çoğu zaman bunu yeniden oluşturamıyorum çünkü tam olarak hangi çevrimdışı düzenleme ve senkronizasyon olayı sırasının çakışmaya yol açtığını bilmiyorum. Senkronizasyon olaylarını daha ayrıntılı bir şekilde kaydetmeye (neler gönderildi, ne alındı, hangi çakışmalar tespit edildi, nasıl çözüldü) ve bu günlükleri gözlemlenebilirlik yığınımıza göndermeye başladım. Yardımcı oluyor ama istediğim kadar temiz değil.
Hızlı kontrol gibi bir şeyle özellik tabanlı testler, CRDT mantığı için gerçekten faydalıdır. Rastgele işlem dizileri oluşturun, bunları rastgele sıralarda uygulayın ve yakınsamayı iddia edin. Keşke bunu daha önce yapmaya başlasaydım.
İzlediklerim Beni EndişelendiriyorBunun nereye varacağı konusunda heyecanlıyım. PGlite (tarayıcıdaki tam Postgres), istemci/sunucu veri katmanı ayrımının ortadan kalktığı bir geleceğe kısa bir bakış gibi geliyor. SQL yazarsınız, her yerde çalışır, senkronizasyon mimari bir karardan ziyade çalışma zamanı meselesidir. Henüz orada değiliz ama buradan görebilirsiniz.
Ayrıca yerel öncelik ile yapay zekanın yakınsamasını da izliyorum. Modelleri yerel olarak çalıştırma, verileri cihazda tutma, bulut yapay zekasını yalnızca açık izinle ve şifrelenmiş verilerle kullanma. Gizlilikle ilgili çıkarımlar ilgi çekici ve bence“verileriniz asla cihazınızdan çıkmıyor”Yapay zeka, yazılım deneyiminin daha fazlasını tükettiğinden, gerçek bir ürün farklılaştırıcısı haline gelecektir.
Beni endişelendiren şeyparçalanma. Her senkronizasyon motoru kendi protokolünü kullanır. Hiçbir standart yok. ElectricSQL kapanırsa (büyük olasılıkla kapanmaz, ancakeğer), PowerSync'e geçiş önemsiz değildir. Senkronizasyon katmanımı kısmen bu nedenle soyutluyorum ama yine de beni tedirgin ediyor.
Web'in neredeyse her şey için standartları vardır. Senkronizasyon için bir tanemiz yok ve yakın zamanda da bir tane çıkacağını sanmıyorum.
Ben de endişeleniyorumkarmaşıklık bütçesi. Önce yerel, gerçek bir mimari karmaşıklık katar: senkronizasyon motorları, çakışma çözümü, istemci tarafı geçişleri, kısmi çoğaltma ve senkronizasyon sınırında kimlik doğrulama. Doğru türde bir uygulama geliştiren deneyimli geliştiricilerden oluşan bir ekip için bu karmaşıklık, karşılığını kat kat amorti eder. Sadece CRUD uygulamasına ihtiyaç duyan bir ekip için bu bir tuzak.
Geçen yıl Berlin'de yerel bir ilk buluşmada Kevin adında bir geliştiricinin bana söylediği bir şeye dönüp duruyorum:
"En iyi mimari, ekibinizin gece saat 2'de hata ayıklayabildiği mimaridir."
O haklı. Yerel öncelikli uygulama, uygulamanızı kullanıcılar için daha hızlı, daha güvenilir ve daha iyi hale getiriyorsa ve ekibiniz senkronizasyonun nasıl çalıştığını anlıyorsa bunu yapın. Kulağa hoş geldiği için ekliyorsanız ve arıza modlarını henüz tam olarak anlamadıysanız, önce bir prototip oluşturun. Nerede kırıldığını öğrenin. O zaman karar ver.
Şu anda dördüncü yerel öncelikli uygulamamı geliştiriyorum: küçük ekipler için, çevrimdışı destek ve isteğe bağlı E2E şifrelemeye sahip, işbirliğine dayalı bir planlama aracı. Bu mimariyle denediğim en iddialı şey bu. Nasıl gittiğini yazacağım.
Yeni başlıyorsanız mevcut uygulamanızda anında yerel okuma ve çevrimdışı yazma özelliklerinden yararlanabilecek bir özellik seçin. Yerel bir SQLite veritabanı ekleyin. Reaktif sorguları bağlayın. Nasıl hissettiğini görün. Sanırım sen de benim verdiğim tepkinin aynısını vereceksin: ah,Buher zaman bu şekilde çalışması gerekirdi.
Ek Okuma- “İklim değişikliği ile mücadele ve uyum çabalarında, kapsamlı ve işlevsel bir uluslararası iş birliğiYerel Öncelikli Yazılım” (Ink & Switch): Bu hala en iyi başlangıç noktasıdır.
- “İklim değişikliği ile mücadele ve uyum çabalarında, kapsamlı ve işlevsel bir uluslararası iş birliğiCRDT'ler: Zor kısımlar”” (Martin Kleppmann, video): Martin'in CRDT'ler hakkındaki konuşmaları mükemmel.
- . localfirstweb.devtopluluk sitesi: İyi bir araç dizini.
- PowerSync Belgeleri
- ElectricSQL Belgeleri
- Yjs Belgeleri
- Otomatik Birleştirme Belgeleri




