# Frontend jako klientowski serwer REST: dyscyplina architektoniczna zamiast chaosu menedżerów stanu
Współczesna development frontendowa dawno przekroczyła etap manipulacji DOM i prostej weryfikacji. Pod względem architektury aplikacje SPA i SSR wykonują tę samą task, co klasyczne systemy serwerowe: przyjmują przychodzące sygnały, przetwarzają dane zgodnie z regułami logiki biznesowej i zwracają gotowy widok. Różnica polega jedynie na środowisku wykonania i protokole interakcji. Zamiast nieustannego wymyślania nowych abstrakcji pod każdy release frameworka, efektywniej zastosować sprawdzone wzorce serwerowe bezpośrednio do kodu klientowego.
Ewolucja roli: od hipermedii do renderowania po stronie klienta
Początkowo web działał według przejrzystego schematu: przeglądarka żądała URL, serwer generował HTML, klient wyświetlał wynik. Niesamowite, ale standardowy HTML jest natywną implementacją zasady HATEOAS. Serwer zwraca nie tylko dane, ale stan interfejsu wraz z dostępnymi przejściami: linkami, formularzami z określonymi metodami, przyciskami akcji. Klient nie przechowuje mapy tras z góry — reaguje na to, co jest dozwolone w bieżącym kontekście.
Współczesne podejście z JSON API jedynie zmieniło format przekazywania stanu, ale nie jego istotę. Tę samą zasadę można wyrazić za pomocą formatu HAL:
{
"id": 42,
"status": "pending",
"_links": {
"self": { "href": "/orders/42" },
"cancel": { "href": "/orders/42/cancel", "method": "POST" },
"detail": { "href": "/orders/42/detail" }
}
}
Wzór UI = f(state) istniał na długo przed pojawieniem się wirtualnego DOM. Wcześniej stan był przekazywany z zdalnego serwera w całości, teraz jest obliczany lokalnie i aktualizowany przyrostowo. Problem nie powstał ze zmianą paradygmatu, lecz z utratą dyscypliny architektonicznej przy przenoszeniu logiki na klienta.
Wydarzenia jako żądania: mapowanie warstw architektonicznych
W developmentce backendowej interakcja jest ściśle uregulowana. Kontroler przyjmuje żądanie HTTP, weryfikuje dane wejściowe, przekształca je w DTO i deleguje przetwarzanie do warstwy serwisowej. Serwis pracuje z modelem domenowym, a repozytorium odpowiada za trwałość. Granice odpowiedzialności są wyraźne i nie zależą od konkretnego języka czy frameworka.
We frontendzie działania użytkownika (kliknięcia, wprowadzanie, przewijanie, wiadomości z WebSocket) pełnią rolę pełnego odpowiednika żądań HTTP. Event = Request. Jednak zamiast ujednoliconych kontrolerów ekosystem wytworzył zoo terminów: store'y, composable, hooki, atomy, runy, view-modelle. Każdy narzędzie narzuca swoją filozofię zarządzania stanem, rozmywając granice architektoniczne. Logiczniej dostosować architekturę klientową do standardu serwerowego:
Frontend
웃 → Event → Controller → Service → Repository → → → → → → Rest API
│ │ │ ↑ │ │
│ │ │ │ └─ Builds HTTP Request │
│ │ │ │ │
│ │ │ └─ Business logic │
│ │ │ │
│ │ └─ Extracts, validates & maps to DTO │
│ │ │
│ └─ Environment detail - Browser Event │
│ │
│ │
│ │
└─ ← Response ← Controller ← Service ← Repository ← ← ← ← ← ← Rest API
│ │ │
│ │ └─ Maps to Domain Model
│ │
│ └─ Maps to framework format: JSX/Vue template
│
└─ Automatically mapped to HTML chunk from JSX by framework
Kontrolery z stanem i szczegóły infrastruktury
Główne zastrzeżenie wobec bezpośredniego przenoszenia wzorców serwerowych dotyczy stanu. Klasyczne kontrolery REST są projektowane jako bezstanowe dla poziomego skalowania. Frontend z definicji działa w kontekście stateful: sesja użytkownika, lokalny cache, stan formularzy i elementów UI żyją w pamięci przeglądarki. To nie sprzeczność, lecz cecha środowiska. W backendzie podobne wzorce stateful stosuje się w gamedevie, bramkach IoT i serwisach streamingowych opartych na gRPC lub WebSockets.
Hooki, sygnały i reaktywne atomy należy traktować wyłącznie jako szczegóły infrastruktury. To mechanizmy, które framework wykorzystuje do efektywnej aktualizacji DOM i śledzenia zależności. Błąd polega na tym, że programiści zaczynają przechowywać reguły biznesowe i logikę walidacji bezpośrednio w tych prymitywach. Menedżery stanu często maskują przeciekające abstrakcje, pozwalając komponentom UI bezpośrednio zależeć od struktury magazynu. Wyodrębnienie warstwy kontrolerów i serwisów rozwiązuje ten problem, izolując logikę biznesową od reaktywnego systemu frameworka.
Praktyczne zastosowanie dyscypliny serwerowej
Wdrożenie podejść backendowych nie wymaga rezygnacji z React, Vue czy Solid. Chodzi o strukturyzację kodu wewnątrz ekosystemu. Dla osiągnięcia przewidywalności i zmniejszenia obciążenia poznawczego przy onboardingu nowych programistów wystarczy przestrzegać kilku zasad:
- Ścisłą walidację przychodzących wydarzeń i danych z API na granicy aplikacji (użycie Zod, Yup lub strażników TypeScript).
- Przekształcanie surowych odpowiedzi serwera i danych wejściowych użytkownika w DTO przed przekazaniem do warstwy biznesowej.
- Izolację logiki domenowej w czystych serwisach, które nie importują hooków frameworka i nie wiedzą o istnieniu DOM.
- Użycie repozytoriów do abstrakcji nad źródłami danych (LocalStorage, IndexedDB, REST, GraphQL).
- Kontrolery pełnią rolę jedynego punktu połączenia: mapują wydarzenia na wywołania serwisów, a wyniki — na propsy dla komponentów prezentacyjnych.
Takie podejście czyni kod agnostycznym wobec frameworków. Logika biznesowa łatwo pokrywa się testami jednostkowymi bez mocków renderera, a migracja z jednego rozwiązania UI na inne dotyka tylko cienkiej warstwy adapterów.
Co ważne
- Aplikacja frontendowa architektonicznie jest równoważna serwerowi REST rozłożonemu w przeglądarce: wydarzenia zastępują żądania HTTP, a renderowanie pełni rolę formowania odpowiedzi.
- HTML historycznie realizuje zasady HATEOAS, przekazując klientowi stan i dostępne akcje jednocześnie.
- Różnorodność terminów (store'y, composable, atomy) tworzy iluzję nowości, ale często rozmywa granice odpowiedzialności i komplikuje utrzymanie kodu.
- Hooki i sygnały to infrastrukturalne prymitywy frameworka, a nie miejsce na przechowywanie reguł biznesowych czy skomplikowanej walidacji.
- Stosowanie dyscypliny serwerowej (DTO, kontrolery, serwisy, repozytoria) zmniejsza sprzężenie kodu, upraszcza testowanie i czyni architekturę niezależną od konkretnego frameworka UI.
— Editorial Team
Brak komentarzy.