Elegir una estructura de carpetas para React: siete diseños y sus puntos críticos.
Compare los diseños planos, basados en tipos, basados en características, Atomic Design, DDD, Feature-Sliced Design y monorepo para React, y conozca el nivel de escala en el que cada uno deja de funcionar.
Fuentes: React Folder Structure: 7 Ways to Organize a React App (And Exactly When Each One Breaks)
Cada base de código de React en crecimiento llega eventualmente a la misma pregunta: ¿dónde debe ir este archivo? No existe una respuesta oficial, pero los equipos pasan entireos sprints migrando entre diferentes estructuras, y los entrevistadores siguen preguntándoles a los candidatos al respecto. Esta guía compara siete formas comunes de organizar una aplicación React, desde una carpeta src/ simple hasta un monorepo con múltiples aplicaciones, utilizando una aplicación de referencia en todo momento. Para cada estructura verás qué es, quién la utiliza realmente, sus puntos fuertes, sus debilidades y el momento exacto en que deja de funcionar, para que puedas elegir la estructura más adecuada para la aplicación que realmente tienes.
Nadie oficial lo elegirá por ti
La documentación antigua de React contaba con una página sobre la estructura de archivos. Allí se indicaba que React no tiene opinión alguna sobre cómo colocar los archivos en carpetas, y se aconsejaba a quienes iniciaran un proyecto que no dedicaran más de cinco minutos a elegir la estructura. Más tarde, esa página fue eliminada cuando la documentación se trasladó a react.dev, y nada la reemplazó. Hoy en día React prácticamente no dice nada sobre las carpetas. La antigua guía sobrevive en la página de Preguntas Frecuentes archivada.
Los frameworks y herramientas relacionados con React también son igualmente vagos al respecto. Next.js se describe a sí mismo como no teniendo opinión alguna sobre cómo se organizan y colocan los archivos de un proyecto. La plantilla para React de Vite genera solo unos pocos archivos fuente y no incluye carpetas. Ninguna autoridad dentro del ecosistema indica qué hacer al respecto.
Esa ausencia explica mucho. Estructuras como Bulletproof React han acumulado decenas de miles de estrellas en GitHub principalmente por proponer un esquema de carpetas, y los desarrolladores discuten constantemente sobre este tema. La forma honesta de enmarcar todo lo que sigue es considerar que estas siete estructuras son convenciones, no estándares. Ninguna de ellas cuenta con el respaldo del propio React. Lo importante es comprender los costos de cada una y, sobre todo, dónde fallan, porque esa es la parte que se vuelve decisiva una vez que existe una aplicación real.
Los dos principios detrás de cada diseño
Los inventores son más relajados que los evangelistas
Las personas que crearon estas metodologías tienden a adoptarlas con mucha más flexibilidad que quienes las promueven. Brad Frost, creador de Atomic Design, publicó “Extending Atomic Design” en 2019. En él describe cómo personas lo contactan para “corregir” su terminología, insiste en que el diseño atómico no es un dogma rígido y dice que los equipos que desean nueve etapas nombradas en honor a miembros de la banda Chicago pueden hacerlo. Puedes leerlo en Extending Atomic Design. El creador del método te dice que los nombres no importan, mientras que en algún lugar una revisión de código se ha estancado por decidir si un componente cuenta como molécula.
La documentación antigua de React también mencionaba por nombre el Diseño Atómico. Se señalaba que algunos desarrolladores dividían los componentes en carpetas según su función dentro de la aplicación, se citaba al Diseño Atómico como ejemplo de ese enfoque y se recomendaba tratar tales metodologías como ejemplos útiles y no como reglas estrictas. Proviendo de la propia documentación de React, esta es la crítica mejor fundamentada sobre el Diseño Atómico estricto en React que existe.
Arquitectura estridente
En realidad solo existe un argumento importante en este debate, y es más antiguo que React. En septiembre de 2011, Robert C. Martin publicó Screaming Architecture. Su pregunta era: cuando alguien mira los directorios de nivel superior de tu aplicación, ¿ve inmediatamente un sistema de atención médica, un sistema contable o un sistema de inventario? Su conclusión fue que una arquitectura debe informar al lector sobre el sistema, no sobre los frameworks que casualmente utiliza.
Esa idea subyace en cada diseño basado en características. Un src/ que contiene components/, hooks/, utils/ y services/ indica que se trata de una aplicación React. Un src/ que contiene checkout/, billing/ y inventory/ revela en qué consiste el negocio. Toda estructura descrita en esta guía es o bien una variación de esa idea o bien un rechazo deliberado de ella.
Colocación
El segundo principio es el que tiene más importancia en la práctica diaria. Kent C. Dodds lo denomina colocación: mantener el código lo más cerca posible del lugar donde resulta relevante. Él atribuye a Dan Abramov una formulación más precisa, según la cual los elementos que cambian juntos deben encontrarse lo más cerca posible entre sí. El argumento completo se encuentra en Colocación.
Visto desde esa perspectiva, cada estructura de esta guía responde a una pregunta: ¿qué partes de tu código evolucionan como una unidad? Si respondes correctamente, las carpetas casi se organizan solas. Si lo haces mal, ninguna metodología podrá salvarte.
Una aclaración sobre una cita popular: el consejo de “mover archivos hasta que parezca correcto” se atribuye ampliamente a Dan Abramov y a veces se cita como documentación oficial de React. En realidad no es parte de la documentación; esta contiene en su lugar consejos de cinco minutos, y la frase de Abramov proviene de un tuit que parece haber sido eliminado. La idea es válida; considera esa cita como un relato popular.
Una aplicación de referencia, siete configuraciones
Comparar diseños solo es justo si la aplicación permanece constante; de lo contrario, terminas comparando ejemplos en lugar de estructuras. Por eso, cada diseño a continuación organiza la misma pequeña aplicación de comercio electrónico con los mismos archivos:
- Funcionalidades: autenticación (iniciar sesión y registrarse), catálogo (lista de productos y detalles), carrito, proceso de pago y pedidos.
- Primitivas de interfaz:
Button,Input,Modal,BadgeySpinner. - Hooks:
useAuth,useCart,useProducts,useCheckoutyuseDebounce. - Módulos de API:
authApi,productsApi,checkoutApi,ordersApiy unapiClientcompartido. - Otros: una
cartStorey una función auxiliarformatPrice.
Cuatro de esos archivos son deliberadamente problemáticos, porque revelan en qué punto cada diseño comienza a fallar:
Modal.jsxrecibe la etiqueta de átomo, molécula u organismo según quien lo clasifique, lo cual enfatiza la taxonomía de Atomic Design.useDebounce.jses completamente genérico, pero solo la búsqueda de catálogo lo utiliza. ¿Es un gancho global o un gancho de catálogo?CartTotal.jsxse renderiza dentro del panel del carrito así como en la pantalla de pago, por lo que no está claro quién es su responsable.formatPrice.jsparece una utilidad trivial, a menos que el dinero sea un concepto real en este dominio; en ese caso debería tratarse de un objeto de valorMoney, y colocarlo en una carpeta de utilidades es un error.
Tenga presentes esos cuatro puntos. Cada estructura los resuelve de manera diferente, y esas diferencias son precisamente lo que distingue un diseño del otro.
Estructura plana
Qué es
Todo se encuentra directamente en src/, sin carpetas o casi ninguna, ordenado alfabéticamente porque nada más impone un orden.
Nadie lo publicó como metodología. Simplemente es donde comienza todo proyecto y donde un número sorprendente de ellos permanece. Incluso la documentación heredada de React lo recomendó como punto de partida: si se siente atascado, guarde todos los archivos en una sola carpeta, y cuando esta crezca lo suficiente como para necesitar dividirla, habrá aprendido qué archivos tiende a editar juntos. Ese es realmente un buen consejo. Una estructura derivada de patrones de cambio observados es mejor que una impuesta desde el primer día.
Quién la utiliza
Prototipos, pruebas rápidas, demos en entorno de prueba, proyectos de hackathon, tareas para llevar a casa y herramientas internas con un único mantenedor. También describe, de forma discreta, numerosas aplicaciones en producción que nunca fueron refactorizadas.
La plantilla oficial de Vite para React es esencialmente sencilla. Al ejecutar npm create vite@latest -- --template react-ts se generan main.tsx, App.tsx, App.css, index.css y una carpeta assets/, sin carpetas components/ ni features/. Dado que el scaffold no expresa opinión alguna, el debate simplemente se traslada a cada equipo que lo utiliza.
Fortalezas
- La navegación no cuesta nada. Hay una sola carpeta, y el selector de archivos encuentra cualquier archivo por su nombre. Al no haber decisiones sobre la ubicación, tampoco hay discusiones al respecto.
Debilidades
- Deja de escalar abruptamente alrededor de 30 archivos. El problema no es gradual: un día la lista de archivos ya no cabe en la barra lateral y la forma de la aplicación desaparece.
- El orden alfabético no tiene sentido.
authApi.jsse encuentra junto aApp.jsxsimplemente porque ambos comienzan con A, mientras queuseAuth.jsyLoginForm.jsx, que siempre se editan juntos, terminan muy separados. El orden de clasificación va en contra de la colocación lógica. - No existen límites que imponer. Cualquier archivo puede importar a cualquier otro, las dependencias circulares se introducen sin que se note, y para cuando se quieren establecer límites, cada violación ya tiene impacto real.
- Eliminar una función se convierte en un trabajo de investigación. Borrar el proceso de pago implica leer archivos para determinar cuáles le pertenecen, ya que no hay una carpeta que eliminar.
Dónde falla
Aproximadamente con 30 archivos, o en el momento en que se une un segundo desarrollador, este segundo umbral cobra más importancia. Un diseño plano funciona porque una sola persona tiene el esquema mental completo. Con dos personas hay dos esquemas, y estos divergen de inmediato.
Estructura basada en tipos
Qué es
Los archivos se agrupan según su tipo: los componentes juntos, los hooks juntos, las llamadas a la API juntas y los almacenes también juntos. La documentación antigua de React describía esto como agrupar por tipo de archivo y lo presentaba como uno de los dos enfoques comunes, sin favorecer a ninguno. Para la aplicación de referencia, eso significa que Button, Modal, CartItem, CheckoutForm y todos los demás componentes se encuentran en components/, todos los hooks en hooks/, todos los módulos de API en api/ y cartStore en store/.
Dado que la mayoría de los tutoriales lo enseñan, también es la estructura que más a menudo encontrarás en proyectos reales.
Quién lo utiliza
Casi todos los tutoriales de React, la mayoría de los proyectos de bootcamp y una gran parte de las aplicaciones en producción surgieron cuando el diseño plano ya no era suficiente sin un plan previo. Es el paso más sencillo de seguir: una vez que src/ se llena de elementos, crear una carpeta components/ es la reacción natural inicial.
Fortalezas
- No necesita explicación alguna. Nunca se ha tenido que indicar a nadie qué debe ir en
hooks/; por lo tanto, los nuevos desarrolladores pueden contribuir desde el primer día utilizando un vocabulario que ya conocen. - La clasificación es sencilla. Un componente va en
components/, un hook va enhooks/, y el propio archivo indica cuál es. Compárese esto con Atomic Design, donde incluso personas sensatas pueden debatir sobre un único componente durante horas. - Es perfectamente adecuado para proyectos a pequeña escala. Funciona bien con menos de unos 50 archivos, y migrar a otro sistema costaría más de lo que aportaría.
Debilidades
- Anuncia el framework en lugar del producto, que es precisamente lo que critica Screaming Architecture. Las carpetas de nivel superior llamadas
components/,hooks/,api/yutils/le indican al principiante que se utiliza React, pero no revelan nada sobre la función de la aplicación. Una tienda en línea y un programador de citas hospitalarias se ven idénticas desde la raíz.
CartItem.jsx en components/, useCart.js en hooks/, cartApi.js en api/ y cartStore.js en store/. Eso significa cuatro directorios y cuatro posiciones de desplazamiento para un único cambio lógico. La política establecida rompe con el principio de colocación cercana: los archivos que con mayor probabilidad cambiarán en la misma actualización se ubican lo más lejos posible entre sí.components/ se convierte en una nueva carpeta plana. En la aplicación de referencia alberga aproximadamente 18 archivos sin ningún tipo de agrupación interna. El problema de la estructura plana se resuelve para los ganchos y módulos de API, pero vuelve a aparecer dentro de la carpeta que contiene la mayor parte del código.ProductCard importe useCheckout; las carpetas son puramente estéticas.Dónde falla
Aproximadamente con 100 archivos, o una vez que components/ contenga unas 20 entradas. La señal de advertencia es de tipo comportamental: los desarrolladores comienzan a añadir prefijos a los nombres de los archivos, como CartItem, CartDrawer y CartTotal, para simular el agrupamiento que las carpetas no ofrecen. Esos prefijos son la forma en que el código base solicita carpetas específicas para cada función.
Estructura basada en funciones
Qué es
Los archivos se agrupan según su función. Cada funcionalidad alberga sus componentes, ganchos, llamadas a la API y estado en un mismo lugar; las primitivas compartidas se encuentran en una zona común, y las funcionalidades se componen a nivel de aplicación sin importarse entre sí.
La documentación tradicional de React denominaba a este agrupamiento por funcionalidades o rutas y ofrecía una forma práctica de determinar los límites. Dado que el significado de “funcionalidad” no es universal, usted mismo elige el nivel de detalle; y cuando no se le ocurre una lista de carpetas de nivel superior, sus usuarios pueden describir las partes principales del producto y utilizar su modelo mental como guía. Probablemente ese sea el consejo práctico más útil de todo este tema: sus usuarios ya saben dónde se encuentran los límites de sus funcionalidades.
En efecto, se trata de Screaming Architecture traducido a React, y hoy en día es la opción principal. En la aplicación de referencia, features/cart/ alberga los componentes del carrito, useCart la API del carrito y cartStore; features/checkout/ contiene CheckoutForm, useCheckout y checkoutApi; y así sucesivamente, con Button, Modal y las demás primitivas en una carpeta compartida llamada components/.
¿Quién lo utiliza?
La implementación de referencia más conocida es Bulletproof React de Alan Alickovic, que contaba con aproximadamente 35.5k estrellas en GitHub y seguía siendo mantenida activamente en el momento de redactar este texto. Es la estructura de React más copiada hasta la fecha, y el documento de estructura del proyecto Bulletproof React merece ser leído directamente en lugar de depender de resúmenes de terceros.
Su directorio src/ recomendado consta exactamente de once carpetas de nivel superior, listadas a continuación con su función respectiva. Cabe señalar que solo una de ellas, features, está organizada por dominio; el resto son capas compartidas de las que se sirven las funcionalidades, además de la capa app que compone todo el sistema.
src
+-- app # application layer (routes, app.tsx, provider.tsx, router.tsx)
+-- assets # static files such as images, fonts, etc.
+-- components # shared components used across the entire application
+-- config # global configurations, exported env variables etc.
+-- features # feature based modules
+-- hooks # shared hooks used across the entire application
+-- lib # reusable libraries preconfigured for the application
+-- stores # global state stores
+-- testing # test utilities and mocks
+-- types # shared types used across the application
+-- utils # shared utility functions
Dentro de cada funcionalidad pueden haber hasta siete segmentos: api, assets, components, hooks, stores, types y utils. La documentación indica claramente que una funcionalidad solo necesita los segmentos que realmente utiliza.
Dos reglas son las principales para lograrlo:
- Ninguna importación entre funcionalidades. Bulletproof React desaconseja que una funcionalidad importe otra y recomienda componer las funcionalidades a nivel de aplicación, de modo que cada una permanezca independiente y el código sea más fácil de seguir.
- Flujo unidireccional. El código debe moverse en una sola dirección, desde el código compartido hasta las funcionalidades y finalmente a la aplicación (
compartido -> funcionalidades -> aplicación).
Ambas reglas pueden aplicarse con la regla de ESLint import/no-restricted-paths. Ese paso de aplicación es el que muchas equipos omiten, y suele ser la razón por la cual sus límites se van debilitando con el tiempo.
Archivos barrel invertidos: Bulletproof React anteriormente recomendaba los archivos barrel, un
index.jsque reexporta la interfaz pública de una función. Ahora recomienda importar los archivos directamente, ya que estos pueden interferir con el proceso de optimización en Vite y causar problemas de rendimiento. Si aprendiste este patrón a partir de una guía antigua, el consejo ha cambiado. Dado que los archivos barrel eran la forma en que la mayoría de los equipos definían la API pública de una función, abandonarlos significa que ahora los límites se definen exclusivamente en la configuración de lint.
Fortalezas
- La colocación conjunta se logra de forma automática. Un cambio en un archivo afecta solo a esa carpeta, ya que los archivos que cambian juntos se encuentran en el mismo lugar.
rm -rf. Elimina features/checkout/, corrige las importaciones de las que se queja el compilador y listo. Esto es más importante de lo que parece: el código difícil de eliminar suele quedar sin borrar nunca.auth/, catalog/, cart/, checkout/ y orders/ comprende el producto antes incluso de abrir un solo archivo.Debilidades
- “Feature” no tiene una definición fija, por lo que habrá desacuerdos al respecto.
CartTotal.jsxse renderiza tanto en la cesta de compras como en el proceso de pago. Ninguna respuesta es correcta; simplemente hay que tomar una decisión y probablemente se volverá a reconsiderar más adelante. - El código compartido requiere una decisión cada vez que se utiliza. Actualmente,
useDebouncesolo se emplea en la búsqueda de catálogos. Si lo hacemos global, la carpeta de hooks compartidos se llenará de elementos utilizados una sola vez. Es mejor mantenerlo enfeatures/catalog/hooks/; cuando el sistema de facturación lo necesite, habrá que duplicarlo, importarlo entre las diferentes funciones (lo cual está prohibido) o promoverlo a un nivel superior. - Dividir el proyecto en funciones demasiado pronto causa aún más problemas que abstraerlo antes de tiempo. Agrupar una aplicación con 40 archivos en cinco carpetas de funciones solo añade procedimientos innecesarios, no estructura real.
Dónde falla
Por lo general no falla, y por eso es la opción predeterminada. Se vuelve problemática en dos situaciones: primero, cuando una sola funcionalidad crece mucho; una carpeta checkout/ con 80 archivos es simplemente una estructura basada en tipos un nivel por debajo. Segundo, cuando surge un modelo de dominio real que no coincide con las funcionalidades de la interfaz. Estas dos presiones dan lugar a las tres estructuras siguientes.
Atomic Design
Qué es
Atomic Design es la metodología de Brad Frost, presentada en una entrada de blog de junio de 2013 y desarrollada en un libro de 2016. Define cinco niveles. Parafraseando sus definiciones:
- Los átomos son los bloques básicos con los que se construye toda interfaz.
- Las moléculas son grupos bastante simples de elementos de UI que funcionan juntos como una unidad.
- Los organismos son componentes más complejos formados por moléculas, átomos e incluso otros organismos.
- Las plantillas son estructuras a nivel de página que colocan los componentes en un diseño y describen la estructura de contenido subyacente.
- Las páginas son instancias concretas de plantillas rellenas de contenido real y representativo.
El detalle que casi todos pasan por alto es la propia descripción de Frost sobre su propósito. Él presenta el Diseño Atómico como un modelo mental y no como un proceso lineal: una forma de ver una interfaz tanto como un todo coherente como un conjunto de partes al mismo tiempo. Nunca fue una estructura de carpetas; la comunidad añadió las carpetas posteriormente.
Quién lo utiliza
Aquí la realidad difiere de la reputación. Hay poca evidencia verificable de que los principales sistemas de diseño de React utilicen una taxonomía de carpetas literal de atoms/, molecules/ y organisms/. Por ejemplo, Shopify Polaris y GitHub Primer no emplean ese nombre de carpetas, a pesar de que los resultados de búsqueda sugieren lo contrario. El caso claramente verificable es Pattern Lab, la herramienta propia de Frost creada para demostrar este método.
Como vocabulario entre los equipos de sistemas de diseño, el Diseño Atómico tiene una gran influencia. Sin embargo, como estructura literal de carpetas en aplicaciones React en producción, la evidencia de su adopción es mucho más escasa de lo que indica su fama. Si alguien afirma que una gran empresa organiza sus carpetas de React de esta manera, pida ver el repositorio.
Fortalezas
- Proporciona a los diseñadores e ingenieros un lenguaje común. Ese es el verdadero beneficio, y las carpetas no tienen ningún papel en ello. Si un diseñador se refiere a un organismo, un ingeniero comprende de inmediato su alcance aproximado, lo cual es valioso en los equipos de sistemas de diseño.
- Fomenta la composición. La jerarquía te impulsa a crear piezas pequeñas y combinarlas, lo cual es una buena práctica en React independientemente de dónde se encuentren los archivos.
- Expone tu inventario de primitivas. Una carpeta
atoms/hace evidente cuando tienes tres variantes de botón que deberían ser una sola.
Debilidades
- La clasificación es realmente difícil, y el creador lo reconoce.
Modal.jsxcombina un fondo, un contenedor, una cabecera y un control de cierre. ¿Se trata de un grupo de átomos y, por lo tanto, de una molécula, o de una pieza compleja independiente y, por lo tanto, de un organismo? Frost publicó finalmente una regla práctica: si descomponer un componente da como resultado elementos básicos como imágenes, encabezados, párrafos y botones, se trata de una molécula; si da como resultado componentes más pequeños como tarjetas, listas de compartición u objetos multimedia, se trata de un organismo. Una heurística como esa existe únicamente porque el límite es ambiguo, y todo límite ambiguo se convierte en un debate recurrente que no aporta valor alguno al usuario.
Button y CheckoutForm, pero ninguna en absoluto sobre useCheckout, checkoutApi, cartStore o formatPrice. La mitad de los archivos quedan fuera de esta taxonomía, por lo que los equipos terminan teniendo átomos, moléculas y organismos, además de un diseño basado en tipos para todo lo demás, heredando ambos conjuntos de problemas.organisms/, templates/, pages/, hooks/ y api/.Dónde falla
En cuanto sale del sistema de diseño. Ordenar las primitivas por complejidad dentro de packages/ui/ o shared/ui/ es razonable. Pero al aplicarse a toda una aplicación, falla de inmediato, ya que la mayor parte de cualquier app está compuesta por elementos distintos a los componentes de interfaz.
La propia postura de Frost respalda esta interpretación: lo denomina un modelo mental y no un dogma, e invita a los equipos a cambiar el nombre de las etapas. Los equipos que imponen un enfoque estricto basado en átomos y moléculas en una aplicación React son más rígidos que la persona que inventó el método.
Estructura impulsada por el dominio
Qué es
El Diseño Impulsado por el Dominio proviene del libro de Eric Evans publicado en 2003, escrito más de una década antes de que existiera React, y representa un enfoque para modelar dominios empresariales complejos mediante código. Dos de sus conceptos se aplican también al frontend. Un contexto delimitado es una zona del software en la que términos, definiciones y reglas específicas se aplican de manera consistente. El lenguaje ubicuo es el principio según el cual el código debe utilizar el mismo vocabulario que el negocio.
Al aplicarlo a una aplicación React, cada dominio se convierte en una carpeta de nivel superior, dividida internamente en capas:
domain/alberga entidades y objetos de valor: reglas comerciales puras.application/contiene casos de uso.infrastructure/guarda clientes de API y sistemas de almacenamiento.presentation/contiene los componentes de React.
Las dependencias apuntan únicamente hacia adentro, por lo que el dominio no sabe nada sobre React ni HTTP.
Fíjese en lo que ocurre con los archivos problemáticos. formatPrice ya no es una función auxiliar; se convierte en un objeto de valor Money que se encuentra en catalog/domain/. useAuth se reduce a un simple wrapper de capa de presentación sobre el caso de uso loginUser. React se coloca deliberadamente en los márgenes.
Quién lo utiliza
La referencia práctica más sólida es Clean Architecture on Frontend de Alex Bespoyasov, que incluye una implementación completa en React y TypeScript en el repositorio bespoyasov/frontend-clean-architecture. Su explicación sobre la estructura en capas indica que el dominio es la parte que permanece igual independientemente de si se pasa de React a Angular o de cambiar un caso de uso específico.
También vale la pena saber que Manfred Steyer, quizás el defensor más reconocido del DDD en frontend, trabaja principalmente con Angular. El modelo basado en clases y orientado a servicios de Angular, con una fuerte inyección de dependencias, es un entorno más adecuado para el DDD que las funciones y ganchos de React. Antes de adoptar por completo alguna guía sólida sobre DDD en frontend, confirme para qué framework fue escrita.
Este estilo se utiliza en fintech, seguros, salud, logística y software empresarial B2B, donde las reglas de negocio son complejas y existían mucho antes que la interfaz de usuario.
Fortalezas
- La lógica de negocio sobrevive al framework. Una entidad
Cartcon reglasaddItemes simplemente TypeScript; reemplazar React no afectaría la capa de dominio. En un producto cuya expectativa de vida es una década, eso constituye un activo valioso. - Las reglas pueden probarse sin React. No hay nada que renderizar, ni hooks, ni bibliotecas de pruebas, solo funciones y afirmaciones que se ejecutan en milisegundos. Con reglas complejas, eso por sí solo puede justificar esta estructura.
- El código refleja la conversación empresarial. Cuando un experto en el dominio indica que un pedido no puede enviarse hasta que se verifique su dirección, esa regla tiene exactamente un lugar: un archivo llamado
Order.ts.
Debilidades
- El código genérico es considerable y constituye el principal costo. Cada entidad suele incluir un modelo de dominio, un DTO y un mapeador entre ellos: tres archivos y una capa de traducción para lo que una aplicación basada en funcionalidades expresa como una única respuesta API tipada. Multiplique esto por veinte entidades.
- La mayoría de las aplicaciones React carecen de un dominio real. Se trata más bien de una opinión que de un hecho verificable, pero es el punto clave de esta sección. La aplicación React típica muestra datos provenientes de una API gestionada por otro equipo o servicio, y su verdadera complejidad radica en el estado del servidor, el caché, las actualizaciones optimistas y las condiciones de carrera. El DDD no aborda ninguno de estos problemas; una biblioteca para el estado del servidor como React Query resuelve el problema real mejor que una capa de dominio. Si sus entidades son respuestas de API tipadas sin invariantes, la arquitectura solo supone un costo económico sin ofrecer ninguna protección real.
- El frontend en realidad no puede liberarse de su framework. La promesa es la independencia del framework, pero un frontend siempre está vinculado a un modelo de renderizado, ruteo, patrones de gestión de estado, APIs del navegador y herramientas de compilación. La arquitectura limpia traslada esas restricciones a los bordes en lugar de eliminarlas. Eso sí representa una ventaja real, aunque menor de lo que sugiere la promoción.
- Los contextos delimitados de la UI son ambiguos. En el backend, la propiedad de los datos proporciona líneas claras. Un frontend muestra todo lo que necesita una pantalla, y una sola página a menudo abarca tres contextos, por lo que los límites se vuelven subjetivos y cambiantes.
- La adaptación al nuevo entorno lleva tiempo. Un desarrollador de React que nunca haya trabajado con DDD puede necesitar semanas para volverse productivo, y siempre habrá alguien que pregunte por qué existe el mapeador.
Dónde falla
Cuando el dominio existe solo en papel. DDD está diseñado para ser útil frente a la verdadera complejidad empresarial. Si tus invariantes son reales, como el dinero, los contratos, los flujos de trabajo regulados o las máquinas de estado multietapa controladas por reglas, la inversión rinde frutos. Cuando la tarea consiste en mostrar una lista de productos desde un endpoint, estás invirtiendo mucha formalidad para simplemente envolver una llamada a fetch.
Una prueba útil: ¿puedes enumerar tres reglas de negocio más interesantes que una verificación de campos obligatorios? Si no puedes, lo que tienes es una interfaz de usuario, no un dominio.
Diseño por segmentos funcionales
Qué es
Diseño por segmentos funcionales (FSD) es la única estructura aquí que constituye una especificación formal, con versiones establecidas, documentación y un verificador de arquitectura oficial llamado steiger. Las demás son convenciones; FSD está por escrito.
Tiene tres niveles de jerarquía:
- Las capas son las carpetas de nivel superior fijas:
app,pages,widgets,features,entitiesyshared. - Las rebanadas son grupos dentro de una capa, divididos según su significado empresarial, es decir, según lo que el código representa para el producto o la aplicación.
- Los segmentos son grupos dentro de una rebanada, divididos según su finalidad técnica:
ui,api,model,libyconfig.
La regla que lo mantiene unido es que un módulo dentro de una rebanada solo puede importar otras rebanadas si estas se encuentran en capas estrictamente inferiores a la suya. Las importaciones siempre apuntan hacia abajo, y dos rebanadas que comparten una capa nunca pueden importarse entre sí. Los elementos hermanos tienen un acoplamiento nulo, algo que se garantiza mediante un analizador de código en lugar de por buenas intenciones.
Dos correcciones a afirmaciones que circulan en otros lugares merecen ser conocidas:
- La capa
processesestá obsoleta. La documentación indica que la especificación actual recomienda evitarla y trasladar su contenido afeaturesyapp. Las guías que aún enumeran siete capas, incluidaprocesses, describen una versión más antigua. - La capa
pagesno fue obsoleta; fue elevada de categoría. La versión 2.1, lanzada en noviembre de 2024, lleva como título principal “¡Las páginas primero!”. La guía actual sugiere mantener más código dentro de las páginas: grandes bloques de interfaz, formularios y lógica de datos que ninguna otra página reutilice deben permanecer en la sección correspondiente a esa página.
En la versión de FSD del aplicación de referencia, CheckoutForm.tsx se encuentra en pages/checkout/ui/ y no dentro de una división por funcionalidades. El enfoque de v2.0 habría dividido este archivo en entidades y funcionalidades por principio, mientras que v2.1 lo mantiene dentro de la página hasta que otra página realmente necesite parte de él.
La motivación para este cambio es lo interesante. La descomposición basada en entidades de v2.0 generó poca cohesión: rastrear un flujo de usuario significaba recorrer muchas carpetas, y era difícil detectar código obsoleto. Los mantenedores admitieron efectivamente que la versión anterior requería demasiadas decisiones arquitectónicas antes de escribir cualquier código. Por lo tanto, la crítica más severa contra FSD fue planteada y resuelta por sus propios autores. Cabe reconocer el esfuerzo por implementar la corrección, pero los equipos que adoptaron v2.0 tuvieron que asumir las consecuencias de ello.
Quién lo utiliza
La situación de adopción requiere una mirada sincera. En el momento de redactar este texto, la página principal de FSD cuenta con una sección destinada a las empresas que utilizan FSD en la que no se menciona a ninguna, sino que invita a los visitantes a presentar a su empresa.
La página oficial de ejemplos enumera alrededor de 21 proyectos, en su mayoría demos y aplicaciones para aficionados como un clon de RealWorld, un juego de Sudoku, una tienda virtual simulada y un pequeño juego para navegador. Las entradas que parecen ser software de producción son pocas, entre ellas Roke.to, Moke Smoke, Moonlogs y una extensión para Chrome.
Los indicadores medibles, hasta el momento de escribir, son modestos pero reales: aproximadamente 297 repositorios de GitHub utilizan el tema feature-sliced-design, el repositorio de documentación cuenta con unos 2.300 “estrellas” y el herramienta de análisis tiene alrededor de 388. La comunidad está centrada en el mundo de desarrolladores de habla rusa, lo que hace que la adopción en inglés parezca menor de lo que probablemente sea.
No se ha podido confirmar, a través de fuentes primarias, que alguna empresa occidental grande y reconocible haya adoptado FSD, ni existe documentación oficial que mencione al creador de la metodología. Esto es inusual para algo tan formalizado y merece ser tenido en cuenta al tomar una decisión de adopción.
Fortalezas
- Elimina los debates sobre dónde colocar algo, ya que la especificación ya ha decididolo. ¿Dónde va este archivo? La especificación lo indica. En equipos grandes, esto ahorra tiempo real, y es precisamente aquí donde FSD supera claramente a un diseño basado en características, en el cual la definición de cada característica se vuelve a revisar constantemente.
- Los límites son verificados por máquinas.
steigery la configuración de ESLint aplican las reglas de importación, y una arquitectura sometida a esas reglas evoluciona mucho más lentamente que una sin ellas. - Se adapta a equipos grandes con poca coordinación. Las capas fijas y la ausencia de importaciones entre elementos relacionados permiten que los equipos trabajen en paralelo sin conflictos.
product es algo que existe; add-to-cart es algo que hace el usuario. Son tipos diferentes de código, y la mayoría de los diseños los confunden.Debilidades
- La curva de aprendizaje es empinada, y la propia página de motivación de FSD reconoce este problema general, señalando que los enfoques existentes tienen un umbral de entrada alto que dificulta la contratación. FSD no es una excepción: capas, rebanadas y segmentos son tres taxonomías que hay que aprender antes de poder colocar un solo archivo.
- El cambio a v2.1 indica inestabilidad. La unidad principal de descomposición cambió, y cualquiera que trabaje según las especificaciones debe seguir sus migraciones.
- Su propia documentación lo considera excesivo para proyectos pequeños, indicando que es innecesario en proyectos de corta duración o cuando la velocidad es más importante que la calidad del código.
- La evidencia de adopción es escasa. Eso está bien para un proyecto personal, pero resulta más difícil de vender cuando un interesado pregunta quién más lo utiliza en producción.
- Migrar una base de código heredada es costoso. Adaptar capas y componentes al código existente constituye un proyecto grande con pocos beneficios visibles.
Dónde falla
En equipos pequeños y aplicaciones de corta duración, donde la formalidad supera al beneficio real. FSD está dirigido a productos grandes y duraderos con muchos colaboradores que realmente necesitan límites estrictos. Para proyectos más pequeños, un diseño basado en funcionalidades ofrece la mayor parte de los beneficios con una fracción de los conceptos.
Estructura modular y monorepos
Qué es
A diferencia de los demás, este caso no se refiere a la organización de src/, por lo que se aborda al final. Se trata del diseño de un repositorio: varias aplicaciones y paquetes compartidos en un único repositorio, coordinados por un ejecutor de tareas.
La documentación de Turborepo describe la estructura canónica: primero se colocan las aplicaciones y servicios en apps/, y todo lo demás, como bibliotecas y herramientas, en packages/. Consulte la guía de Turborepo sobre diseñar un repositorio.
Cada aplicación dentro de apps/ sigue necesitando una de las otras seis estructuras internamente. Un monorepo es independiente de la forma en que se organiza una sola aplicación, y confundir ambos conceptos es el malentendido más común en este tema.
En el momento de redactar este texto, las herramientas principales eran turbo (2.10.x, aproximadamente 73 millones de descargas mensuales), nx (23.x, aproximadamente 38 millones) y lerna (9.x, aproximadamente 7.5 millones). Verifique las cifras actuales antes de confiar en ellas. Contrariamente a una creencia generalizada, Lerna no está obsoleta. Nx asumió su gestión en 2022, cuando corría el riesgo de dejar de mantenerse. Continúa siendo distribuido y ahora delega en Nx las tareas de ejecución y caché, mientras se concentra en la gestión de versiones y publicaciones.
¿Quién lo utiliza?
Las discusiones sobre monorepos suelen mencionar a Google, Meta y Microsoft, lo cual es engañoso. Se trata de monorepos a nivel corporativo, no de monorepos para React, y no dicen nada sobre su aplicación React.
Las cifras siguen siendo interesantes. Se informó que el repositorio de Google contaba con aproximadamente mil millones de archivos y 86 TB (Potvin y Levenberg, Communications of the ACM, julio de 2016). Meta describe su repositorio gestionado por Sapling como que alberga decenas de millones de archivos, commits y ramas. Según Brian Harry en mayo de 2017, el repositorio de Windows en Microsoft contenía unos 3,5 millones de archivos que sumaban 300 GB, y en su desarrollo participaban aproximadamente 4.000 ingenieros.
Cada una de esas empresas tuvo que crear sus propias herramientas para hacer frente a la situación: Google desarrolló Piper y Bazel, Meta construyó Sapling y EdenFS, y Microsoft creó GVFS. Considérelo una señal de costos, no un respaldo. Si un monorepo necesita un equipo dedicado a la infraestructura para funcionar, eso dice algo sobre los monorepos.
Los ejemplos relevantes y verificables de React son diferentes:
- El repositorio de Vercel para
next.jses un verdadero monorepo de React, con directoriosapps/ypackages/de nivel superior, espacios de trabajo pnpm y 19 paquetes, entre ellosnext,create-next-appyeslint-config-next. - Shopify almacena sus aplicaciones React Native en un único monorepo que abarca Shop, Inbox, Point of Sale, Shopify Mobile y Local Delivery. Su equipo de ingeniería explicó en septiembre de 2021 que un monorepo único reduce los costos de mantenimiento, ya que hay un solo proceso por mejorar y optimizar en lugar de tener que gestionar muchos paquetes y repositorios separados.
Ese es el verdadero caso de uso de un monorepo, y cabe señalar que se refiere al proceso de mantenimiento, no a la organización del código.
Fortalezas
- Los cambios entre paquetes son atómicos. Modificar
packages/ui/Buttony las tres aplicaciones que lo utilizan se realiza en un único commit, una sola solicitud de pull request, una revisión y una ejecución de CI. Con repositorios separados, eso se convierte en un aumento de versión, una publicación y tres actualizaciones de dependencias. Esta es la razón por la que existen los monorepos. - El compartir cuenta con límites reales.
packages/uitiene su propiopackage.jsony una API pública explícita, y los límites entre paquetes son el tipo más estricto que ofrece JavaScript. - Existe una sola versión de todo: ninguna aplicación queda atascada en React 18 mientras otra utiliza la versión 19; hay un único archivo de bloqueo y una única fuente de verdad.
- Los grandes refactores se vuelven factibles. Puedes cambiar el nombre de una API en todas las aplicaciones que la utilizan en un único commit, con el compilador verificando el resultado.
Debilidades
- La infraestructura es un costo real y permanente: espacios de trabajo, gráficos de tareas, caché remoto y matrices de CI. Los propios documentos de Turborepo reconocen el problema de escalabilidad, ya que cada espacio de trabajo conlleva sus propias tareas de pruebas, validación y compilación, y un único monorepo puede terminar con miles de ellas. Turborepo existe para resolver el problema que generan los monorepos.
- La CI se vuelve más lenta y costosa antes de que mejore su velocidad. Sin una detección de cambios afectados y un caché adecuadamente configurados, cada solicitud de integración ejecuta todo el proceso.
- La incorporación de nuevos usuarios es más difícil. Antes de realizar el primer commit, un empleado nuevo debe aprender cómo se resuelven los espacios de trabajo, cómo funciona el gráfico de tareas y cómo se comporta el caché.
packages/ui tiene tres consumidores, cualquier cambio corre el riesgo de causar regresiones en las tres aplicaciones. Los equipos suelen responder agregando propiedades y variantes hasta que el componente compartido se convierte en un lenguaje de configuración que nadie entiende.Dónde falla
Cuando solo hay una aplicación. Un monorepo cuya carpeta apps/ contiene una sola aplicación paga el costo total de las herramientas sin obtener ningún beneficio: no hay código que compartir ni cambios entre aplicaciones que se puedan realizar de forma atómica.
En cuanto a los umbrales, no existe una cifra oficial. Turborepo y Nx no publican ningún tamaño de equipo ni número de aplicaciones a partir del cual la adopción resulte rentable. Las cifras precisas que circulan en línea, como un número específico de ingenieros que supuestamente se requieren para usar Turborepo, provienen de contenido SEO de baja calidad y no de documentación del proveedor ni de investigaciones serias. Cualquiera que cite un umbral exacto está inventándolo.
Una regla práctica, presentada como opinión y no como hecho: adopte un monorepo cuando tenga dos aplicaciones reales que compartan código de verdad, o una sola aplicación junto con un sistema de diseño que utilicen equipos externos. Fuera de eso, los costos superan los beneficios, y su propia situación podría cambiar esa línea divisoria.
Los React Server Components no determinan la estructura de carpetas
Muchos artículos recientes afirman que los Componentes del Servidor de React cambian la forma en que se deben estructurar las carpetas, recomendando típicamente directorios separados components/server/ y components/client/. Nada en la documentación oficial lo respalda.
La directiva use client marca un límite en el grafo de módulos, no en el árbol de carpetas. La guía de renderizado de Next.js relevante describe use client como una forma de declarar un límite entre los grafos de módulos del servidor y el cliente, y explica que una vez que un archivo lleva esta directiva, sus importaciones y los componentes hijos que renderiza directamente terminan todos en el paquete del cliente.
Ese segundo punto es el crucial. El límite se propaga a través de las importaciones, no de las carpetas. Un archivo dentro de un directorio client/ no es un componente del cliente por su ubicación, y un archivo dentro de server/ no recibe protección alguna por ese nombre. Lo que determina el resultado es la directiva junto con la cadena de importaciones.
Si desea imponer este control, las herramientas son los paquetes npm server-only y client-only, que hacen que la compilación falle cuando el código cruza el límite incorrecto. Las carpetas no ofrecen esa funcionalidad.
La guía oficial, de hecho, aboga en la dirección opuesta: mover 'use client' hacia abajo en la estructura, hacia los componentes más simples, para mantener pequeño el paquete del cliente. Una división a nivel superior en carpetas de servidor y cliente fomenta el instinto contrario.
Varias convenciones del App Router sí afectan la organización, y son reales:
- Los grupos de rutas, escritos como
(carpeta), organizan las rutas sin cambiar la URL. - Las carpetas privadas, escritas como
_carpeta, excluyen por completo la posibilidad de enrutamiento. - La colocación es segura por defecto, ya que una ruta solo se vuelve accesible públicamente una vez que se agrega un archivo
page.jsoroute.js. Los componentes, pruebas y utilidades pueden encontrarse directamente dentro de los segmentos de ruta sin convertirse en rutas, por lo que no se requieren carpetas privadas para ello.
Además, Next.js indica claramente que no tiene opiniones sobre cómo organizar y colocar los archivos del proyecto. Su documentación presenta tres estrategias de organización sin clasificarlas.
Cómo elegir
Aquí está la decisión en forma condensada, con los argumentos más importantes:
- Menos de unos 30 archivos, o si aún estás descubriendo el problema: mantén la estructura simple. Una estructura basada en patrones reales de cambios es mejor que una supuesta desde el primer día, tal como sugerían los documentos antiguos de React.
- Menos de unos 50 archivos, un equipo pequeño y una vida útil corta: el enfoque basado en tipos es adecuado. Está pasado de moda, pero eso no importa; migrar costaría más de lo que ahorraría.
- Cualquier proyecto real y de larga duración: opta por el enfoque basado en características. Debería ser la opción predeterminada. Satisface los principios de la Arquitectura que Grita, respeta la colocación lógica de los componentes, hace que eliminar características sea sencillo y permite establecer límites mediante herramientas de validación. Comienza con las once carpetas de Bulletproof React, agrega zonas
import/no-restricted-pathsdesde el principio y omite los archivos tipo barrel.
apps/ y packages/, gestionado por Turborepo o Nx, y permita que cada aplicación elija su propia estructura interna de esta lista.Hay una regla que prevalece sobre todas estas: los enfoques se combinan. Los repositorios de código reales los mezclan, y la documentación antigua de React lo señalaba claramente, indicando que los proyectos más grandes suelen utilizar una combinación de enfoques en la práctica, por lo que elegir el “correcto” desde el principio no es muy importante.
Una configuración de producción perfectamente razonable es un monorepo que contenga aplicaciones basadas en funcionalidades, un paquete de sistema de diseño organizado aproximadamente por líneas atómicas, y una capa de dominio en la zona donde se encuentran las reglas comerciales reales. Nadie evalúa su arquitectura en términos de pureza.
Puntos clave
Las personas más cercanas a estas convenciones las aplican de manera flexible y siguen revisándolas públicamente. Frost denomina al Atomic Design un modelo mental y no un dogma. La documentación de React limitó la toma de decisiones a cinco minutos y luego eliminó esa página. Next.js y Vite se niegan a tomar una posición al respecto. FSD cambió su unidad principal de descomposición en la versión 2.1, y Bulletproof React revirtió su recomendación sobre los archivos en barril. Cambiaron de rumbo porque escucharon los comentarios reales en lugar de defender una postura fija.
Eso deja dos reglas duraderas, ambas anteriores a cualquier layout mencionado aquí:
- Arquitectura evidente: las carpetas de nivel superior deben indicarle a un nuevo ingeniero qué hace el producto, no qué framework utiliza.
- Colocación conjunta: los elementos que cambian juntos deben encontrarse lo más cerca posible entre sí, dentro de lo razonable.
Toda estructura en esta guía busca cumplir con esas dos reglas a una escala específica. Elija la opción más económica que las satisfaga para la aplicación que tiene actualmente, no la que espera tener en el futuro. Los indicadores de que es hora de pasar a otra etapa son claros: prefijos en los nombres de archivos que sustituyen a carpetas, cuatro carpetas abiertas para un solo cambio, o una hora perdida debatiendo si algo es una molécula. Actúe ante esos indicadores cuando aparezcan, y no antes.