Design patterns

Șabloanele de proiectare folosite în Recash și problemele pe care le rezolvă.

Recash folosește mai multe șabloane de proiectare consacrate, fiecare ales pentru o problemă concretă din aplicație, nu de dragul teoriei:

  • Singleton — o singură conexiune la baza de date și la Redis, refolosită peste tot
  • Publish/Subscribe — evenimentele live circulă prin canale Redis, fără legături directe între servicii
  • Cache-aside — citirile costisitoare trec întâi prin cache
  • Middleware — verificarea accesului, într-un singur loc, la marginea aplicației
  • Observer — mai multe componente ascultă aceleași evenimente și se actualizează singure când acestea sosesc
  • Provider (Context) — starea comună este injectată o singură dată și disponibilă oriunde, fără să fie pasată manual
  • Adapter — autentificarea comunică cu baza de date printr-o piesă standard, interschimbabilă
  • Stale-while-revalidate — interfața afișează imediat datele din cache, apoi le reîmprospătează în fundal
  • Optimistic concurrency — o modificare de stare reușește doar dacă starea nu s-a schimbat între timp

Singleton

Clientul Prisma și clientul Redis se creează o singură dată și se refolosesc în toată aplicația. Contează mai ales în dezvoltare: Next.js reîncarcă codul la fiecare salvare de fișier, iar fără acest tipar, fiecare reîncărcare ar deschide o conexiune nouă către baza de date, până la epuizarea celor disponibile.

Publish/Subscribe

Când ceva trebuie livrat instant — o notificare, un mesaj de chat — aplicația Next.js nu trimite nimic direct către client. Publică un eveniment pe un canal Redis, iar serverul WebSocket, care ascultă acel canal, se ocupă de livrare. Cele două servicii rămân independente: niciunul nu depinde de acțiunile celuilalt. Detalii complete în Comunicare în timp real.

Cache-aside

Când cineva cere date costisitoare — clasamentul, un profil public — aplicația verifică întâi dacă rezultatul există deja în Redis. Dacă există, îl returnează direct, fără să atingă baza de date. Dacă nu, îl calculează, îl salvează în Redis cu un termen de expirare și abia apoi îl returnează — următoarele cereri îl vor găsi gata pregătit prin Redis.

Middleware

Fiecare cerere trece printr-un punct unic de control înainte să ajungă la orice pagină sau rută API. Se verifică dacă utilizatorul este autentificat și dacă are voie în zona cerută: paginile personale cer cont, iar cererile fără drept de acces sunt oprite pe loc. Regulile de acces stau într-un singur fișier, nu repetate în fiecare pagină.

Observer

Interfața are o singură conexiune WebSocket, dar mai multe zone care depind de ea: clopoțelul de notificări, chat-ul unei postări, indicatorul de status din header. Fiecare dintre acestea se abonează la evenimentele care o interesează, iar când un eveniment sosește, toate componentele abonate își actualizează conținutul în același moment. Niciuna nu gestionează conexiunea și niciuna nu știe de celelalte — doar reacționează la evenimentele primite.

Provider (Context)

Starea este răspândită pe întreaga aplicație, nu doar pe o singură pagină: cine este utilizatorul conectat, ce limbă și monedă a ales, dacă fereastra de autentificare este deschisă, dacă rulează un ecran de încărcare. Această stare nu este pasată manual din componentă în componentă — un lanț lung și fragil, numit prop drilling — ci furnizată o singură dată, la vârful aplicației, printr-un provider de context. Orice componentă, oricât de adânc îngropată, o poate cere direct de acolo. O schimbare de limbă sau de sesiune se reflectă peste tot deodată, fără a fi transmisă din componentă în componentă.

Adapter

Sistemul de autentificare (Auth.js) nu știe nimic despre structura bazei de date Recash și nici nu trebuie. Legătura dintre ele se face printr-un adaptor — o piesă standard care traduce operațiile de care are nevoie autentificarea („creează un utilizator”, „găsește contul asociat”) în operații concrete pe schema Prisma a aplicației. Dacă stratul de stocare s-ar schimba, s-ar înlocui adaptorul, iar logica de autentificare ar rămâne neatinsă. Mai multe detalii la Autentificare & autorizare.

Stale-while-revalidate

Pe client, datele nu se revalidează la fiecare vizită. O componentă afișează instant ce are deja în cache — chiar dacă acele date sunt vechi (stale) — apoi trimite o cerere de reîmprospătare și, dacă între timp ceva s-a schimbat, actualizează discret datele afișate (revalidate). Utilizatorul vede conținutul imediat, fără ecran gol de așteptare, iar informația rămâne proaspătă. Este perechea de pe client a lui cache-aside de pe server: unul evită recalcularea, celălalt evită reîncărcarea.

Optimistic concurrency

Când două acțiuni pot atinge aceeași postare în același timp — doi colectori care o revendică simultan, o anulare fix când finalizarea este în curs — nu se pornește de la presupunerea că nimic nu s-a schimbat între citire și scriere. Fiecare modificare de stare se face condiționat: reușește doar dacă postarea este încă în starea așteptată, iar dacă între timp altcineva a modificat-o, operația eșuează controlat, în loc să suprascrie orbește. Așa sunt prevenite dublările și cursele, fără a bloca preventiv nimic. Detalii în Protecție împotriva atacurilor.