# Frontend as a Client-Side REST Server: Architectural Discipline Instead of State Management Chaos
Modern frontend development has long outgrown the stage of DOM manipulations and simple layouts. Architecturally, SPA and SSR applications perform the same task as classic server systems: they accept incoming signals, process data according to business logic rules, and return a ready-made view. The difference lies only in the execution environment and interaction protocol. Instead of endlessly inventing new abstractions for every framework release, it's more effective to apply proven server patterns directly to client code.
Evolution of the Role: From Hypermedia to Client-Side Rendering
Originally, the web operated on a transparent scheme: the browser requested a URL, the server generated HTML, and the client displayed the result. Surprisingly, standard HTML is a native implementation of the HATEOAS principle. The server returns not just data, but the interface state along with available transitions: links, forms with specified methods, action buttons. The client doesn't store a route map ahead of timeβit reacts to what's permitted in the current context.
The modern approach with JSON APIs merely changed the state transmission format, but not its essence. The same principle can be expressed through the HAL format:
{
"id": 42,
"status": "pending",
"_links": {
"self": { "href": "/orders/42" },
"cancel": { "href": "/orders/42/cancel", "method": "POST" },
"detail": { "href": "/orders/42/detail" }
}
}
The formula UI = f(state) existed long before the advent of virtual DOM. Previously, state was transmitted entirely from the remote server; now it's computed locally and updated incrementally. The problem didn't arise from the paradigm shift, but from losing architectural discipline when porting logic to the client.
Events as Requests: Mapping Architectural Layers
In backend development, interactions are strictly regulated. The controller accepts an HTTP request, validates input data, transforms it into a DTO, and delegates processing to the service layer. The service works with the domain model, while the repository handles persistence. Responsibility boundaries are clear and independent of the specific language or framework.
In frontend, user actions (clicks, input, scroll, WebSocket messages) serve as a complete analog to HTTP requests. Event = Request. However, instead of unified controllers, the ecosystem has spawned a zoo of terms: stores, composables, hooks, atoms, runes, view models. Each tool imposes its own state management philosophy, blurring architectural boundaries. It's more logical to align client architecture with server standards:
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
Stateful Controllers and Infrastructure Details
The main objection to directly porting server patterns concerns state. Classic REST controllers are designed as stateless for horizontal scaling. Frontend, by definition, operates in a stateful context: user session, local cache, form and UI element states live in browser memory. This isn't a contradiction, but an environment feature. In backend, similar stateful patterns are used in game dev, IoT gateways, and streaming services based on gRPC or WebSockets.
Hooks, signals, and reactive atoms should be viewed solely as infrastructure details. These are mechanisms the framework uses for efficient DOM updates and dependency tracking. The mistake is that developers start storing business rules and validation logic directly inside these primitives. State managers often mask leaky abstractions, allowing UI components to depend directly on the store structure. Introducing a controller and service layer solves this by isolating business logic from the framework's reactive system.
Practical Application of Server Discipline
Adopting backend approaches doesn't require ditching React, Vue, or Solid. It's about structuring code within the ecosystem. To achieve predictability and reduce cognitive load for onboarding new developers, just follow a few principles:
- Strict validation of incoming events and API data at the app boundary (using Zod, Yup, or TypeScript guards).
- Transforming raw server responses and user inputs into DTOs before passing to the business layer.
- Isolating domain logic in pure services that don't import framework hooks and know nothing about DOM.
- Using repositories to abstract over data sources (LocalStorage, IndexedDB, REST, GraphQL).
- Controllers act as the single connection point: they map events to service calls, and results to props for presentational components.
This approach makes the code framework-agnostic. Business logic is easily covered by unit tests without renderer mocks, and migrating from one UI solution to another only affects a thin adapter layer.
Key Takeaways
- A frontend app is architecturally equivalent to a REST server deployed in the browser: events replace HTTP requests, and rendering forms the response.
- HTML has historically implemented HATEOAS principles, delivering state and available actions to the client simultaneously.
- The variety of terms (stores, composables, atoms) creates an illusion of novelty but often blurs responsibility boundaries and complicates maintenance.
- Hooks and signals are framework infrastructure primitives, not places for business rules or complex validation.
- Applying server discipline (DTOs, controllers, services, repositories) reduces code coupling, simplifies testing, and makes architecture independent of specific UI frameworks.
β Editorial Team
No comments yet.