6.8 KiB
Arquitectura frontend
Propósito: explicar cómo está construido el frontend. Este documento, junto con UI system y UI patterns, forma la fuente de verdad (SSOT) de frontend/UI.
1. Stack
El frontend usa React 19, TypeScript estricto, React Router, TanStack Query, React Hook Form, Axios, Tailwind CSS v4, Shadcn/UI y @repo/rdx-ui.
2. Arquitectura por capas
El flujo habitual de una feature es:
API → DTO → adapter → Entity → query hook → controller → page controller → Page → UI
shared/api: transporte y contratos HTTP.shared/adapters: transforma DTOs en entidades de frontend.shared/entities: modelos consumidos por la aplicación.shared/hooks: queries, mutations y query keys de TanStack Query.controllers: orquestación funcional, formularios, navegación y efectos.page controllers: componen el estado completo de una pantalla.Page: conecta controller y componentes; debe tener poco estado propio.ui: renderiza props preparadas y emite eventos.
La UI no debe contener reglas de dominio, fetch manual ni traducciones entre DTO y entidad. Los controllers no deben convertirse en servicios de dominio.
3. Módulos frontend
Cada módulo publica sus rutas y su cliente desde modules/<module>/src/web. Las features se organizan por caso de uso (list, create, update, detail) y comparten transporte, entidades y hooks mediante shared cuando existe reutilización real.
Las dependencias deben apuntar hacia contratos compartidos, no hacia páginas concretas. Un componente de dominio puede envolver una foundation visual si aporta defaults o vocabulario propios, pero no debe recrear el layout de página.
4. Routing y layouts
Las rutas declaran su shell en handle.layout:
auth: login y recuperación, bajoAuthLayout.app-sidebar: dashboard, listados, settings y navegación general.app-fullscreen: tareas focalizadas de create/update/detail y Account cuando la ruta así lo declara.
apps/web/src/routes/app-routes.tsx agrupa las rutas por ese valor y las monta bajo el layout correspondiente. protected y requireCompany expresan guards independientes del shell. La elección debe seguir las rutas productivas del área; no se infiere únicamente por el nombre de la pantalla.
5. Shells de aplicación
Application shell
├── AppSidebarLayout
│ ├── AppSidebar
│ ├── AppTopbar
│ └── AppMain
├── AppFullscreenLayout
│ └── AppMain
└── AuthLayout
AppSidebarLayoutproporciona navegación y topbar.AppFullscreenLayoutocupa el viewport para una tarea focalizada.AuthLayoutcentra las pantallas públicas de identidad.AppMainentrega el espacio disponible. No es propietario del scroll de una página.
Dentro de AppMain, una pantalla usa RdxPageFrame; el sistema visual y sus reglas se documentan en UI system.
AppMain
└── RdxPageFrame
├── header
├── content
│ └── RdxPageContainer
│ └── RdxPageContent
└── footer opcional
RdxPageFrame
→ estructura y scroll
RdxPageContainer
→ ancho y padding
RdxPageContent
→ composición de los hijos principales
Regla: Las Pages no deben reconstruir estas responsabilidades con wrappers HTML locales. En Create/Update, la composición canónica es RdxPageFrame → RdxPageContainer → RdxPageContent: los errores inline y el editor son hijos directos de RdxPageContent; los dialogs son hermanos del frame; RdxPanel se reserva para secciones semánticas reales.
6. Identity y sesión
El runtime actual se compone en apps/web/src/app.tsx:
DataSourceProvider
└── IdentityAuthSessionProvider
└── router
El login usa POST /identity/auth/login. Tras autenticar:
- persiste
accessTokenyrefreshToken; - elimina cualquier empresa activa anterior;
- obtiene la sesión con
GET /identity/auth/session; - carga
GET /companies/available; - resuelve la empresa activa y el destino autenticado.
Al restaurar una sesión, el provider valida el token, hidrata identidad y empresas y limpia ambos estados si la restauración falla. El frontend nuevo usa @erp/identity/client; no debe introducir dependencias nuevas sobre el runtime legacy de auth.
7. Selección de empresa
- Sin empresas: navega a
/no-companies; no existeactiveCompanyIdy el logout permanece disponible. - Una empresa: se selecciona automáticamente.
- Varias empresas: navega a
/company-selectionhasta que el usuario elige. - Empresa persistida no disponible: vuelve a aplicar las tres reglas anteriores.
El switcher autenticado consume availableCompanies, activeCompany y changeActiveCompany() desde useIdentityAuthSession(). No obtiene tokens ni repite la consulta directamente.
8. Contexto tenant y X-Company-Id
Las requests tenant-scoped incluyen X-Company-Id cuando existe una empresa activa. No lo incluyen:
POST /identity/auth/login
POST /identity/auth/refresh
POST /identity/auth/logout
GET /identity/auth/session
GET /companies/available
Los guards de compañía resuelven la navegación; los componentes visuales no deben inspeccionar headers HTTP ni decidir tenancy.
9. Refresh de Axios
Ante un 401 autenticado, el interceptor:
- inicia
POST /identity/auth/refreshsi existe refresh token; - comparte una única
refreshPromiseentre requests concurrentes; - persiste los tokens renovados;
- reintenta una sola vez la request original mediante
_retry; - limpia sesión y navega a
/loginsi el refresh falla.
Login, refresh, logout, session y companies available están excluidos del bucle de refresh y del header tenant. Una request tenant reintentada conserva el contexto de empresa.
10. Navegación y contexto
La navegación derivada de una operación pertenece al controller. Las páginas conectan callbacks y las vistas puras no importan React Router. El retorno desde editores debe reutilizar el mecanismo de returnTo/navegación existente en vez de reconstruir rutas localmente.
El estado remoto permanece en TanStack Query. No se duplica en estado local salvo estado efímero de presentación. Queries, invalidaciones y mutations viven en hooks compartidos del dominio.
11. Reglas de contribución arquitectónica
- Mantener TypeScript estricto y
import typeen cabecera. - Naming y código en inglés; documentación y comentarios arquitectónicos en español.
- Preferir composición a frameworks CRUD o controllers genéricos.
- No introducir acceso a red en UI ni reglas de negocio en Pages.
- Mantener routing, sesión, tenant y sistema visual como responsabilidades separadas.
- Cambios de API visual pública deben sincronizar implementación, UI system, UI Kit y tests.