返回首页

前端作为 REST 服务器:架构与模式

本文探讨了客户端开发和服务器端开发之间的架构相似性。作者展示了如何在前端应用 REST 模式、DTO 和分层架构来减少代码耦合并简化 SPA 维护。

客户端 REST:为前端架构带来秩序
Advertisement 728x90

前端作为客户端 REST 服务器:架构纪律取代状态管理混乱

现代前端开发早已超越了 DOM 操作和简单布局的阶段。从架构角度看,SPA 和 SSR 应用执行的任务与经典服务器系统完全相同:接收输入信号,按照业务逻辑规则处理数据,并返回就绪的视图。区别仅在于执行环境和交互协议。与其为每个框架版本无休止地发明新抽象,不如直接将经过验证的服务器模式应用到客户端代码中。

角色演变:从超媒体到客户端渲染

最初,Web 运作模式非常透明:浏览器请求一个 URL,服务器生成 HTML,客户端显示结果。令人惊讶的是,标准的 HTML 就是 HATEOAS 原则的原生实现。服务器返回的不仅仅是数据,还有接口状态以及可用的转换:链接、带有指定方法的表单、操作按钮。客户端无需提前存储路由图——它只需响应当前上下文允许的操作。

现代使用 JSON API 的方式仅仅改变了状态传输格式,但本质未变。相同原则也可以通过 HAL 格式表达:

Google AdInline article slot
{
  "id": 42,
  "status": "pending",
  "_links": {
    "self":   { "href": "/orders/42" },
    "cancel": { "href": "/orders/42/cancel", "method": "POST" },
    "detail": { "href": "/orders/42/detail" }
  }
}

UI = f(state) 这个公式早在虚拟 DOM 出现之前就已存在。以前,状态完全从远程服务器传输;如今,它在本地计算并增量更新。问题并非源于范式转变,而是将逻辑移植到客户端时失去了架构纪律。

事件即请求:映射架构层

在后端开发中,交互受到严格规范。控制器接收 HTTP 请求,验证输入数据,将其转换为 DTO,并委托服务层处理。服务层操作领域模型,而仓库层负责持久化。职责边界清晰,与具体语言或框架无关。

在前段,用户操作(点击、输入、滚动、WebSocket 消息)完全等同于 HTTP 请求。事件 = 请求。然而,前端生态并未采用统一的控制器,而是衍生出一堆术语:stores、composables、hooks、atoms、runes、view models。每种工具都强加自己的状态管理理念,模糊了架构边界。更合理的做法是将客户端架构与服务器标准对齐:

Google AdInline article slot
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

有状态控制器与基础设施细节

直接移植服务器模式的主要异议涉及状态。经典 REST 控制器设计为无状态,以支持水平扩展。前端本质上运行在有状态上下文中:用户会话、本地缓存、表单和 UI 元素状态驻留在浏览器内存中。这并非矛盾,而是环境特性。在后端,类似的有状态模式常用于游戏开发、IoT 网关以及基于 gRPC 或 WebSockets 的流媒体服务。

Hooks、signals 和响应式 atoms 应仅视为基础设施细节。这些是框架用于高效 DOM 更新和依赖追踪的机制。错误在于开发者开始将业务规则和验证逻辑直接存入这些原语。状态管理器往往掩盖了泄漏抽象,允许 UI 组件直接依赖 store 结构。引入控制器和服务层,通过隔离业务逻辑与框架响应式系统来解决此问题。

服务器纪律的实际应用

采用后端方法无需放弃 React、Vue 或 Solid。这关乎在生态系统中组织代码。为了实现可预测性和降低新开发者的认知负担,只需遵循几条原则:

Google AdInline article slot
  • 在应用边界严格验证传入事件和 API 数据(使用 Zod、Yup 或 TypeScript guards)。
  • 在传递给业务层前,将原始服务器响应和用户输入转换为 DTO。
  • 将领域逻辑隔离在纯服务中,这些服务不导入框架 hooks,且对 DOM 一无所知。
  • 使用仓库抽象数据源(LocalStorage、IndexedDB、REST、GraphQL)。
  • 控制器作为唯一连接点:将事件映射到服务调用,将结果映射到展示组件的 props。

这种方法使代码与框架无关。业务逻辑易于通过单元测试覆盖,无需渲染器模拟,从一种 UI 方案迁移到另一种仅影响薄适配层。

关键要点

  • 前端应用在架构上等同于部署在浏览器中的 REST 服务器:事件取代 HTTP 请求,渲染形成响应。
  • HTML 历史上实现了 HATEOAS 原则,同时向客户端交付状态和可用操作。
  • 各种术语(stores、composables、atoms)制造了新奇幻觉,但往往模糊职责边界并复杂化维护。
  • Hooks 和 signals 是框架基础设施原语,不是存放业务规则或复杂验证的地方。
  • 应用服务器纪律(DTOs、controllers、services、repositories)可降低代码耦合、简化测试,并使架构独立于特定 UI 框架。

— Editorial Team

Advertisement 728x90

继续阅读