1) În ultimii 4-5 ani am experimentat cu o mulțime de arhitecturi de aplicații Solana și m-am gândit că e momentul să scriu concluziile. Începând cu cel mai puțin complicat și adăugând treptat mai multă complexitate. Așadar, iată: Solana Application Architecture, un fir de discuție.
2) Să începem cu modul ușor. Probabil așa arată prima ta aplicație Solana. Un frontend simplu React stabilește o conexiune către un RPC. Nu este nevoie de un server API. RPC-ul este serverul tău. La început, acestea erau pline de getAccountInfos, logică de construcție personalizată etc. Mai târziu, când performanța va avea de suferit, adaugi straturi de cache și batching. Intră în scenă getMultipleAccounts. Poate adaugi și abonamente websocket + polling pentru a actualiza interfața live. Această arhitectură permite prototiparea extrem de rapidă. Practic nu există DevOps. Costuri de server neglijabile (doar implementează pe Vercel, etc). Totuși, are câteva defecte majore.
3) Prima problemă pe care o întâlnești sunt interogările complexe. În această arhitectură, tot ce ai este RPC-ul Solana vanilla. Asta înseamnă că trebuie să tratezi Solana așa cum o expune un RPC – ca pe o bază de date NoSQL cu o problemă de atitudine. Interogările de puncte nu sunt o problemă. Poți chiar să fii foarte creativ cu PDA-urile pentru a parcurge datele programului ca pe o bază de date de grafice. Dar de îndată ce trebuie să faci primul GPA, te așteaptă durere maximă. Interfața nu este deloc ergonomică. Dacă ai orice fel de date cu lungime dinamică, acestea nu pot fi folosite deloc. În funcție de furnizorul tău de RPC, poate fi incredibil de lent. Chiar și fără GPA-uri, această abordare tinde să fie mult mai lentă decât o aplicație web2 tipică, cu randare pe server și o bază de date normală.
4) A doua problemă pe care ai întâlnit-o este portabilitatea logicii de construcție a tranzacțiilor. În această configurare, toată logica pentru construirea tranzacțiilor este fie (a) în codul tău frontend, fie (b) în bibliotecile pe care codul tău le importă. În cazul (a), oricine vrea să construiască tranzacții în afara frontend-ului tău va avea probleme maxime (inclusiv pe tine, când inevitabil vei avea nevoie de mai multă aplicație). În cazul (b) orice modificare a logicii de construcție a tranzacțiilor necesită publicări de biblioteci, actualizarea tuturor pachetelor și apoi actualizări UI. Bazarea puternică pe uneltele Anchor, cum ar fi rezoluția conturilor, poate minimiza cantitatea de logică care trebuie portată – dar problema rămâne. Acest lucru îți lipsește agilitatea și face dificilă schimbarea endpoint-urilor smart contract, asigurându-te în același timp că toate versiunile logicii tale de construcție TX continuă să funcționeze.
5) A treia problemă cu care te confrunți este faptul că interfețele de utilizator sunt, în general, slabe la trimiterea tranzacțiilor, mai ales în cazul logicii de reîncercare. Un utilizator poate naviga departe de pagină, TX încetează să mai încerce. Cantități mari de tranzacții tind să aibă dificultăți în a ajunge. Este dificil să depanezi de la distanță de ce lucrurile nu aterizează.
6) Ultima problemă aici este că nu ești singurul cu această arhitectură. Un RPC este valoros și practic trebuie să expui URL-ul RPC în frontend-ul tău. Acum, ajungi într-un joc de-a șoarecele și pisica ca să te asiguri că nimeni nu-ți fură RPC-ul și îți crește costurile.
7) Și ce urmează? De obicei, chiar dacă nu abordezi portabilitatea transmisiunii, ajungi să fie nevoie să rezolvi întrebările de listă. gPA e prost, și știm cu toții asta. Astfel, poți construi o arhitectură hibridă. În această arhitectură, păstrezi capacitatea de a prototipa rapid, dar trimiți interogările dificile și urâte într-un API. Un exemplu concret este guvernanța – se creează propuneri care au un set de etichete ("Economic", "Tehnic" etc). gPA nu poate filtra după etichete.
8) Această arhitectură nu a rezolvat portabilitatea tranzacțiilor sau oamenii care îți retrag RPC-ul. Dar la scară largă, poți cel puțin să rezolvi problema GPA-urilor lente/imposibile. Introduce totuși o nouă problemă – indexatorii. Mai multe despre asta mai târziu.
9) În final, ai ceea ce aș numi stack-ul Solana "enterprise". Nu mai tratezi Solana ca pe o bază de date NoSQL. În schimb, îl tratezi ca pe un autobuz de evenimente. Frontend-ul nu știe nimic despre modelul de date al Solanei. Serverul construiește tranzacțiile și le transmite interfeței pentru semnare, apoi le trimite către Solana. Tratează acest lucru ca pe un eveniment și așteaptă ca acesta să se propage înapoi către indexatori, ceea ce va schimba datele de bază. Această configurație are o portabilitate excelentă a tranzacțiilor – oricine poate accesa API-ul tău cu parametri curați și poate primi tranzacțiile/instrucțiunile înapoi. Face ca interfața să fie extrem de rapidă – din punctul de vedere al interfeței, practic este web2. Poți profita la maximum de SSR. RPC-ul este abstractizat – nimeni nu-ți poate fura creditele. Dar această configurație are problemele sale
10) Prima problemă cu care te vei confrunta este durerea indexatorului. Deși acest lucru s-a ameliorat în ultimii câțiva ani (datorită echipelor Triton, Helius și StreamingFast), tot ajung să bat indexerul nostru cu o cheie franceză în mod regulat. Vei rata mesaje. Când ratezi un mesaj, interfața ta intră într-o stare ciudată (de exemplu: ți-am trimis un NFT, în lanț l-ai primit, în baza mea de date scrie că încă îl dețin). Astfel de probleme sunt frustrante de depanat. E vina ta? Este furnizorul tău de date? Cine știe! Așa s-a dus după-amiaza ta.
11) Următoarea problemă cu care te vei confrunta este sincronizarea. Când folosești direct RPC pentru orice, îți permit să treci angajamentele și să te ocupi de cele mai noi date. Cu un indexator, totul este manual. Aceasta înseamnă că atunci când construiești tranzacții, le poți construi pe baza unor date depășite. Returnezi o tranzacție sortită eșecului. Poți rezolva problema datelor depășite folosind furnizori de date care îți oferă date extrem de rapide (de exemplu fluxul laser Helius). Dar apoi trebuie să te ocupi manual de reorganizări. Adică, datele pe care le indexezi trebuie să fie neindexate dacă acea transmisie nu a fost procesată. Este un coșmar.
12) Poți "ocoli" problemele de sincronizare construind tranzacții doar folosind date din RPC, folosind doar datele indexate pentru a alimenta interfața. Dar apoi, utilizatorii vor avea în continuare probleme cu posibile inconsistențe între interfață și lanț. Adică frontend-ul spune că dețin acest NFT și pot să-l transfer, apoi backend-ul țipă la mine și spune că nu pot.
13) Ultima problemă cu care te confrunți cu această configurație este costul, iar dacă suntem melodramatice, "moartea descentralizării". Visul web3 era să nu fie nevoie să implementezi o mulțime de servere centralizate. Acum ai implementat suficientă infrastructură pentru a obține un post de lead architect într-un magazin web2. Costă bani. Costă timp. Și totul este foarte centralizat. Cât de descentralizat este protocolul tău dacă cea mai bună modalitate de a interacționa cu el este printr-un API web2? Și sunt cam 72 de dezvoltatori pe Solana care ar ști cum să interacționeze cu el fără acel API.
14) În cele din urmă, nu voi muri pe niciun deal legat de descentralizare. Ceea ce este cel mai bine pentru utilizatori tinde să fie cea mai bună alegere. Configurația "enterprise" duce la aplicații web rapide și moderne și la o separare clară a preocupărilor. Pe de altă parte, adaugă costul devops și te face mai puțin agil. Recomand majoritatea startup-urilor să înceapă cu metoda direct-to-rpc, cu excepția cazului în care construiești ceva care trebuie explicit să fie rapid sau să aibă o semantică complexă de interogare. Momentul de lansare pe piață este esențial. Poți oricând să angajezi un inginer de nivel mediu mai târziu și să-l arunci în temnița de indexare.
15) Fin. Dacă cineva dintre voi a găsit o configurație mai bună, anunțați-vă. Acestea sunt toate lucrurile pe care le-am încercat. Îmi place destul de mult să mă joc cu configurația enterprise în ultima vreme.
50,7K