Writing/SQLite
Două baze de date și o graniță pe care n-o treci niciodată
O aplicație care își livrează propriul conținut are o problemă: fiecare actualizare înlocuiește conținutul acela, iar notițele utilizatorului stau în același fișier.
Construiesc un cititor offline-first care își livrează textul în interiorul aplicației. Fără conturi, fără rețea, fără reclame. Ceea ce ridică o întrebare la care majoritatea aplicațiilor nu trebuie să răspundă niciodată: ce se întâmplă cu evidențierile și notițele cuiva atunci când textul de care sunt legate e înlocuit de o actualizare?
Răspunsul comod e o singură bază de date și o migrare care duce notițele dincolo. Răspunsul corect e două baze de date și o regulă că nimic nu are voie să treacă între ele.
Una e de aruncat, cealaltă e de neînlocuit
Baza de date cu conținut e doar de citit, livrată în binar și înlocuită în întregime la fiecare actualizare. E de aruncat prin construcție. Dacă un build o corupe, următorul o repară și nu pierde nimeni nimic.
Baza de date a utilizatorului stă în documentele aplicației, nu se livrează niciodată și nu se înlocuiește niciodată. Ține evidențieri, notițe, semne de carte și poziția în lectură. E singurul lucru din aplicație care nu poate fi regenerat și aparține unui om, nu unei versiuni.
“O actualizare care poate ajunge la notițele utilizatorului nu e o actualizare. E un risc pe care îl livrezi după program.”
Regula care face lucrul ăsta să țină e că nicio migrare nu are voie să copieze peste graniță. În clipa în care una o face, cele două fișiere sunt cuplate, iar o actualizare devine capabilă să distrugă ani de adnotări. Notițele fac referire la conținut printr-un identificator stabil, nu printr-un rând, așa că textul de dedesubt poate fi înlocuit fără ca referința să însemne altceva.
Testarea SQL-ului prin rularea lui
Repository-urile primesc o interfață mică de bază de date, în loc să apeleze direct biblioteca SQLite a platformei. Nu e stratificare de dragul stratificării. Înseamnă că SQL-ul real, exact așa cum e scris, rulează în teste pe un SQLite în proces, în loc să fie înlocuit cu un mock.
Un mock de bază de date testează că ți-ai apelat corect propriul wrapper. Nu-ți poate spune că o interogare e greșită, iar o interogare greșită e tot riscul. Așa, testele sunt cu câteva milisecunde mai lente și testează lucrul care chiar se poate strica.
Un detaliu care merită furat: căutarea în tot textul e configurată cu diacriticele eliminate, pentru că un cititor român va tasta un cuvânt fără semne diacritice și se așteaptă să-l găsească. Genul ăsta de lucru se verifică empiric pe text real, nu se presupune din documentație, iar de aceea verificarea căutării a existat înaintea oricărui ecran.
