FactuGES_ERP/docs/frontend/frontend-architecture.md

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, bajo AuthLayout.
  • 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
  • AppSidebarLayout proporciona navegación y topbar.
  • AppFullscreenLayout ocupa el viewport para una tarea focalizada.
  • AuthLayout centra las pantallas públicas de identidad.
  • AppMain entrega 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, Detail/Show y LIST, la composición canónica es RdxPageFrame → RdxPageContainer → RdxPageContent: errores, alertas, secciones semánticas y contenido funcional 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:

  1. persiste accessToken y refreshToken;
  2. elimina cualquier empresa activa anterior;
  3. obtiene la sesión con GET /identity/auth/session;
  4. carga GET /companies/available;
  5. 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 existe activeCompanyId y el logout permanece disponible.
  • Una empresa: se selecciona automáticamente.
  • Varias empresas: navega a /company-selection hasta 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:

  1. inicia POST /identity/auth/refresh si existe refresh token;
  2. comparte una única refreshPromise entre requests concurrentes;
  3. persiste los tokens renovados;
  4. reintenta una sola vez la request original mediante _retry;
  5. limpia sesión y navega a /login si 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 type en 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.