Arquitectura de aplicaciones React
Una aplicacion React grande no se mantiene solo con componentes. Necesita estructura, limites claros, gestion de estado, rutas, datos remotos, testing y convenciones.
Objetivo
El objetivo de la arquitectura es que el proyecto pueda crecer sin que cada cambio obligue a tocar media aplicacion.
Preguntas clave:
- Donde viven las paginas?
- Donde viven los componentes reutilizables?
- Donde se llama a la API?
- Donde se guarda estado local, global y remoto?
- Como se testea?
- Como se separa dominio de UI?
Estructura por features
Una estructura habitual:
src/
app/
router.tsx
providers.tsx
features/
auth/
components/
hooks/
services/
types.ts
products/
components/
hooks/
services/
types.ts
shared/
components/
hooks/
lib/
ui/Ventaja: cada feature agrupa su UI, hooks, tipos y servicios.
Estructura por capas
Otra opcion:
src/
components/
pages/
hooks/
services/
stores/
types/Es simple para proyectos pequenos, pero puede degradarse cuando crece mucho.
Regla practica
Para aplicaciones reales, prefiere estructura por dominio o feature. Para demos pequenas, una estructura por capas puede bastar.
Separar UI y datos
Un componente de pagina puede coordinar datos:
function ProductsPage() {
const { data, isLoading } = useProducts()
if (isLoading) return <Spinner />
return <ProductsList products={data} />
}El componente visual recibe props:
function ProductsList({ products }: { products: Product[] }) {
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
)
}Esto facilita pruebas y reutilizacion.
Providers
Centraliza providers:
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
{children}
</AuthProvider>
</QueryClientProvider>
)
}Evita esconder providers dispersos en muchas paginas.
Rutas
Agrupa rutas por dominio:
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{ path: "products", element: <ProductsPage /> },
{ path: "orders", element: <OrdersPage /> }
]
}
])Dependencias entre modulos
Regla saludable:
app -> features -> shared
features no deberian depender entre si sin motivo claro
shared no deberia depender de featuresAnti-patrones
- Componentes enormes que hacen fetch, validan, renderizan y transforman datos.
- Estado global para todo.
- Hooks que ocultan demasiados efectos secundarios.
- Carpeta
utilsconvertida en cajon desastre. - Imports circulares entre features.
Checklist
- Cada feature tiene ownership claro.
- La UI reutilizable no conoce APIs concretas.
- Los servicios de API estan centralizados.
- Los providers estan declarados en un sitio.
- Los tests pueden montar componentes sin levantar toda la app.
