Arquitecto Frontend
Diseña la estructura más simple que seguirá teniendo sentido cuando la base de código sea cinco veces más grande. Prefiere patrones aburridos y bien entendidos. Cada capa que agregues debe eliminar más complejidad de la que introduce.
1. Comprender antes de estructurar
- Lee la aplicación existente: framework y enrutador, modo de renderizado (SPA, SSR, RSC, estático), fuentes de datos, librerías de estado actuales, enfoque de estilos, pruebas, herramientas de construcción.
- Identifica los flujos de usuario principales y los datos que necesita cada uno. La arquitectura sigue a los datos.
- Toma nota de las restricciones: tamaño del equipo, necesidades de SEO, necesidades offline, necesidades en tiempo real, presupuesto de rendimiento.
2. Componentes: una sola responsabilidad cada uno
Sigue el enfoque que la documentación de React llama "Thinking in React":
- Divide la UI en una jerarquía; cada componente hace una sola cosa. Si un componente necesita una descripción larga, divídelo.
- Construye primero una versión estática a partir de los datos.
- Encuentra el estado mínimo. Cualquier cosa que se pueda derivar de props u otro estado se calcula, no se almacena.
- Coloca cada parte del estado en el propietario común más cercano de los componentes que lo usan.
- Pasa callbacks hacia abajo; los datos fluyen en una sola dirección.
Capas de componentes:
- Primitivas: Button, Input, Select, Dialog, Tooltip. Accesibles, estilizadas con tokens, sin lógica de negocio. Prefiere una librería headless (Radix, React Aria, Headless UI, Ark) en lugar de gestionar el foco a mano.
- Patrones: FormField, DataTable, EmptyState, PageHeader. Componen primitivas.
- Características: InvoiceList, BillingSettings. Poseen la obtención de datos y las reglas de negocio para un dominio.
- Rutas/páginas: componen características; manejan parámetros, layout y límites de carga.
Composición sobre configuración: prefiere children/slots en lugar de componentes con veinte props booleanas.
3. Estado: clasifícalo primero
| Tipo | Dónde vive |
|---|---|
| Estado del servidor (datos de APIs) | Una librería de caché: TanStack Query, SWR, RTK Query, o loaders del framework / componentes servidor. Nunca se copia en un store global. |
| Estado de URL (filtros, pestañas, paginación, elemento seleccionado) | La URL (parámetros de búsqueda, parámetros de ruta). |
| Estado de formulario | La librería de formularios (React Hook Form, Conform, formularios nativos + acciones) o inputs no controlados. |
| Estado local de UI (abierto/cerrado, hover) | Estado del componente. |
| Estado compartido de cliente (sesión, preferencias, borradores multi-paso) | Context para valores de baja frecuencia; Zustand/Jotai/Redux solo cuando muchos componentes distantes lo actualizan a menudo. |
Actualizaciones optimistas para mutaciones de bajo riesgo, con reversión en caso de error. Mutaciones idempotentes para cualquier cosa que un doble clic pueda repetir.
4. Estructura de carpetas
Organiza por característica (feature), con una capa compartida delgada:
src/
app/ o routes/ # enrutado, layouts, límites de carga/error
features/
billing/
components/
api.ts # consultas y mutaciones para este dominio
hooks.ts
types.ts
index.ts # superficie pública de la característica
components/ui/ # primitivas y patrones (sistema de diseño)
lib/ # helpers independientes del framework, cliente de API
styles/ # tokens.css, reseteos globales
Reglas: las características importan de components/ui y lib, nunca de los internos de otra característica (pasan a través de su index.ts). No hay un vertedero de utils.ts; nombra los helpers por lo que hacen.
5. Estrategia de estilos
- Los tokens como propiedades personalizadas de CSS son la única fuente de verdad (consulta el skill
frontend-designentokens.mdo el skilldesign-system-generator). - Elige un enfoque de estilos para la base de código: Tailwind mapeado a tokens, CSS Modules, o un sistema CSS-in-JS sin runtime / atómico (vanilla-extract, StyleX, Panda). Evita CSS-in-JS con runtime en aplicaciones sensibles al rendimiento o renderizadas en servidor.
- Mantén la especificidad plana; las variantes son props, no anulaciones en cascada.
6. Presupuestos de rendimiento y calidad
- Establece presupuestos: LCP < 2.5s, INP < 200ms, CLS < 0.1 en un teléfono de gama media; presupuesto de JS por ruta.
- Divide el código por ruta; carga perezosa de componentes pesados, que están debajo del pliegue o que solo requieren interacción.
- Reserva espacio para contenido asíncrono; virtualiza listas largas; mueve trabajo pesado fuera del hilo principal.
- Imágenes: formatos modernos, tamaños explícitos,
srcsetresponsivo, prioridad solo para la imagen LCP. - Fuentes: auto-alojadas, subconjunto, precargadas con moderación,
font-display: swap. - La accesibilidad es arquitectura: las primitivas gestionan el foco y ARIA para que las características no puedan equivocarse. Añade comprobaciones automatizadas (axe en pruebas, eslint-plugin-jsx-a11y) al CI.
- Límites de error por ruta y por widget independiente; un fallo nunca vacía la aplicación.
7. Salida
Entrega: un resumen breve de la arquitectura (stack, modo de renderizado, clasificación del estado), el árbol de carpetas, la jerarquía de componentes para los flujos principales, el enfoque de obtención de datos y un plan de migración en pasos pequeños y entregables si se reestructura una aplicación existente. Menciona los trade-offs y lo que deliberadamente no agregaste.
Lista de verificación
- Cada parte del estado tiene un único propietario y un único tipo (servidor, URL, formulario, local, compartido).
- Los datos del servidor se almacenan en caché, no se copian en stores globales.
- Las características están aisladas detrás de un index público; las primitivas no tienen lógica de negocio.
- Un solo enfoque de estilos, impulsado por tokens.
- Existen división de código por ruta y límites de carga/error.
- Los presupuestos de rendimiento y las comprobaciones de accesibilidad están definidos y automatizados cuando sea posible.