Newsletter para devsEntra

Cómo organizar las carpetas de un proyecto de JavaScript en 8 pasos

Una buena organización del proyecto es esencial para crear código escalable, fácil de mantener y colaborativo.

Sin una estructura clara, el código puede volverse difícil de navegar, complicando la implementación de nuevas funciones y la resolución de errores.

En este artículo, exploraremos cómo organizar un proyecto de programación, desde estructuras básicas hasta ejemplos más avanzados y complejos.

Con pequeñas modificaciones también vale para organizar en carpetas proyectos de TypeScript.

Cada semana, experiencias y aprendizaje sobre desarrollo web e IA en tu bandeja de entrada.

Únete a más de 5.800 developers que ya reciben la newsletter.

Suscríbete gratis →

¿Por qué es importante organizar tu proyecto?

Organizar tu proyecto correctamente te ofrece múltiples beneficios:

  • Facilita la navegación: Una estructura clara te permite encontrar rápidamente los archivos y las partes del código que necesitas modificar.
  • Mejora la colaboración: Otros desarrolladores pueden unirse al proyecto y entender dónde hacer cambios sin dificultad.
  • Simplifica el mantenimiento: Cuando necesitas corregir errores o añadir funciones, una estructura organizada reduce la posibilidad de cometer errores.
  • Facilita la escalabilidad: A medida que el proyecto crece, una buena organización te permite agregar nuevas funciones sin crear caos.

Vamos a ver diferentes ejemplos, desde estructuras básicas hasta complejas, adaptadas a distintos tipos de proyectos.

Organizar un proyecto JS por pasos

Paso 1: Proyecto básico de JavaScript

En proyectos pequeños o simples, como scripts de automatización o pequeños programas, no necesitas una estructura elaborada. Aquí tienes un ejemplo de una estructura básica:

miProyecto
├── index.js
├── config.js
├── utils.js
└── README.md

Explicación:

  • index.js: Archivo principal que ejecuta el código.
  • config.js: Contiene configuraciones y valores constantes.
  • utils.js: Incluye funciones auxiliares reutilizables.
  • README.md: Documentación básica del proyecto.

Este tipo de estructura es ideal para scripts pequeños y herramientas simples.

Paso 2: Proyecto web básico

Para un proyecto web simple que utiliza HTML, CSS y JavaScript, una estructura organizada puede ser así:

miProyectoWeb
├── index.html
├── styles
│   └── styles.css
├── scripts
│   └── main.js
└── assets
    └── logo.png

Explicación:

  • index.html: El archivo HTML principal.
  • styles: Carpeta para archivos CSS.
  • scripts: Carpeta para archivos JavaScript.
  • assets: Carpeta para imágenes, fuentes u otros recursos.

Esta estructura es sencilla pero permite mantener separados los estilos, el código JavaScript y los recursos multimedia.

Paso 3: Proyecto Node.js sencillo

Si estás trabajando en una aplicación de servidor con Node.js, aquí tienes una estructura básica:

miProyectoNode
├── src
│   ├── index.js
│   ├── config.js
│   └── utils.js
├── package.json
└── README.md

Explicación:

  • src: Carpeta que contiene el código fuente.
  • index.js: Punto de entrada de la aplicación.
  • config.js: Configuraciones y constantes del proyecto.
  • utils.js: Funciones auxiliares.
  • package.json: Archivo de configuración de npm para gestionar dependencias.

Esta estructura es ideal para proyectos pequeños o prototipos en Node.js.

Paso 4: Proyecto React

En proyectos de React, una estructura clara ayuda a manejar componentes, estilos y datos. Aquí tienes una estructura recomendada para aplicaciones React:

miProyectoReact
├── public
│   └── index.html
├── src
│   ├── components
│   │   ├── Button.js
│   │   └── Header.js
│   ├── pages
│   │   ├── Home.js
│   │   └── About.js
│   ├── App.js
│   ├── index.js
│   └── styles.css
├── package.json
└── README.md

Explicación:

  • public: Archivos públicos accesibles directamente.
  • components: Componentes reutilizables de la interfaz de usuario.
  • pages: Componentes que representan páginas completas.
  • App.js: Componente raíz de la aplicación.
  • index.js: Punto de entrada de la aplicación React.
  • styles.css: Archivo CSS global.

Esta estructura facilita la reutilización de componentes y permite escalar la aplicación.

Paso 5: Proyecto de API REST con Node.js y Express

Cuando construyes una API REST, necesitas organizar tu código para manejar rutas, controladores y lógica de negocio. Aquí tienes una estructura típica:

miApiRest
├── src
│   ├── controllers
│   │   ├── userController.js
│   │   └── productController.js
│   ├── models
│   │   ├── userModel.js
│   │   └── productModel.js
│   ├── routes
│   │   ├── userRoutes.js
│   │   └── productRoutes.js
│   ├── services
│   │   ├── userService.js
│   │   └── productService.js
│   ├── index.js
│   └── config.js
├── package.json
└── README.md

Explicación:

  • controllers: Contiene la lógica para manejar las solicitudes HTTP.
  • models: Define la estructura de los datos y las interacciones con la base de datos.
  • routes: Define las rutas de la API.
  • services: Contiene la lógica de negocio y funciones auxiliares.
  • index.js: Punto de entrada de la API.
  • config.js: Configuraciones y valores constantes.

Esta estructura permite mantener la lógica de negocio, el manejo de rutas y la interacción con la base de datos claramente separadas.

Paso 6: Proyecto complejo (Aplicación Full Stack)

Para proyectos grandes y complejos, como una aplicación full stack con frontend y backend, la estructura puede ser más avanzada:

miProyectoFullStack
├── client
│   ├── public
│   ├── src
│   │   ├── components
│   │   ├── pages
│   │   ├── App.js
│   │   └── index.js
│   └── package.json
├── server
│   ├── controllers
│   ├── models
│   ├── routes
│   ├── services
│   ├── index.js
│   └── config.js
├── .env
├── docker-compose.yml
├── package.json
└── README.md

Explicación:

  • client: Contiene el frontend de la aplicación (React, Angular, etc.).
  • server: Contiene el backend (Node.js, Express, etc.).
  • .env: Variables de entorno.
  • docker-compose.yml: Archivo de configuración para Docker.
  • package.json: Maneja dependencias compartidas entre frontend y backend.

Esta estructura permite trabajar de manera independiente en frontend y backend, lo que facilita el desarrollo y la colaboración.

En la primera parte de este artículo cubrimos desde ejemplos básicos hasta una estructura completa para una aplicación Full Stack. Ahora, vamos a expandir esos ejemplos con estructuras aún más detalladas y avanzadas que son utilizadas en proyectos de gran escala.

Paso 7: Arquitectura basada en capas (Clean Architecture)

La arquitectura basada en capas, también conocida como Clean Architecture, es un patrón diseñado para maximizar la mantenibilidad y escalabilidad de una aplicación. Este enfoque divide tu proyecto en capas independientes, cada una con responsabilidades claras y separadas, lo que permite desarrollar, probar y escalar más fácilmente.

Estructura del proyecto

miProyectoCleanArchitecture
├── domain
│   ├── entities
│   └── repositories
├── application
│   ├── use-cases
│   └── dto
├── infrastructure
│   ├── controllers
│   ├── models
│   ├── routes
│   └── database
├── config
│   └── env.js
├── tests
├── package.json
└── README.md

Desglose de la estructura:

  1. domain: Es la capa central que contiene las reglas de negocio y no tiene dependencias externas. Aquí definimos:

    • entities: Clases que representan los objetos del dominio (por ejemplo, User, Product). Definen las propiedades y métodos relacionados con el negocio.
    • repositories: Interfaces que definen cómo interactuar con los datos (por ejemplo, IUserRepository). No contienen implementación concreta.
  2. application: Esta capa contiene los casos de uso (o “use cases”), que representan las acciones específicas del negocio.

    • use-cases: Funciones o clases que implementan la lógica del negocio (por ejemplo, CreateUser, UpdateProduct).
    • dto (Data Transfer Objects): Objetos que se utilizan para transferir datos entre capas, facilitando la validación y transformación de datos.
  3. infrastructure: Contiene la implementación concreta y los detalles técnicos de la aplicación.

    • controllers: Manejan las solicitudes HTTP y actúan como punto de entrada para la aplicación.
    • models: Definen los esquemas de la base de datos (por ejemplo, usando MongoDB o Sequelize).
    • routes: Define las rutas de la API y conecta los controladores con los casos de uso.
    • database: Configuración y conexión a la base de datos.
  4. config: Almacena configuraciones, como variables de entorno y archivos de configuración específicos de la aplicación.

  5. tests: Carpeta para almacenar pruebas unitarias y de integración.

Ejemplo de implementación:

domain/entities/User.js

class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }
}

module.exports = User;

domain/repositories/IUserRepository.js

class IUserRepository {
  create(user) {
    throw new Error('Method not implemented.');
  }

  findById(id) {
    throw new Error('Method not implemented.');
  }
}

module.exports = IUserRepository;

application/use-cases/CreateUser.js

class CreateUser {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  execute(userData) {
    const user = new User(userData.id, userData.name, userData.email);
    return this.userRepository.create(user);
  }
}

module.exports = CreateUser;

infrastructure/controllers/UserController.js

const CreateUser = require('../../application/use-cases/CreateUser');
const UserRepository = require('../models/UserRepository');

class UserController {
  static async createUser(req, res) {
    const userRepository = new UserRepository();
    const createUser = new CreateUser(userRepository);
    const userData = req.body;

    try {
      const user = await createUser.execute(userData);
      res.status(201).json(user);
    } catch (error) {
      res.status(400).json({ message: error.message });
    }
  }
}

module.exports = UserController;

Ventajas de esta arquitectura:

  • Alta mantenibilidad: Cada capa tiene responsabilidades claras, lo que facilita la localización de errores y la implementación de nuevas funciones.
  • Pruebas fáciles: El desacoplamiento de la lógica de negocio y la infraestructura facilita la prueba de casos de uso independientes.
  • Escalabilidad: Permite añadir nuevas funcionalidades o cambiar la infraestructura (por ejemplo, cambiar de base de datos) sin afectar la lógica del negocio.

Paso 8: Monorepo usando Yarn o NPM Workspaces

En proyectos grandes con múltiples aplicaciones o paquetes, un monorepo permite gestionar todo el código en un solo repositorio. Usar herramientas como Yarn Workspaces o NPM Workspaces facilita compartir código y configuraciones, lo que mejora la productividad y la consistencia.

Estructura del proyecto

miMonorepo
├── packages
│   ├── common
│   │   ├── index.js
│   │   └── package.json
│   └── ui-components
│       ├── Button.js
│       ├── Modal.js
│       └── package.json
├── apps
│   ├── client
│   │   ├── src
│   │   ├── public
│   │   └── package.json
│   └── server
│       ├── src
│       ├── controllers
│       └── package.json
├── package.json
└── README.md

Configuración del Monorepo con Yarn Workspaces:

package.json (raíz)

{
  "name": "miMonorepo",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ]
}

Ventajas del Monorepo:

  • Gestión centralizada: Todas las dependencias y scripts se gestionan desde la raíz del repositorio.
  • Reutilización de código: Los paquetes como common y ui-components se comparten fácilmente entre las aplicaciones del monorepo.
  • Consistencia: Al tener un solo repositorio, es más fácil mantener configuraciones uniformes y aplicar cambios globales.

Ejemplo de uso:

apps/client/src/App.js

import Button from 'ui-components/Button';

function App() {
  return (
    <div>
      <h1>Hola desde Monorepo</h1>
      <Button label="Clic aquí" />
    </div>
  );
}

export default App;

Ejecución del Monorepo:

Para instalar todas las dependencias, ejecuta:

yarn install

Para ejecutar el cliente:

yarn workspace client start

Para ejecutar el servidor:

yarn workspace server start

Herramientas útiles:

  • Yarn Workspaces: Facilita la gestión de dependencias y la ejecución de scripts en un monorepo.
  • Lerna: Complemento para Yarn o NPM Workspaces que ayuda a gestionar versiones y despliegues en monorepos grandes.
  • Turbo Repo: Herramienta para acelerar la ejecución de scripts y la gestión de caché en monorepos.
  • Portless: Si te cansas de recordar qué puerto corresponde a cada servicio del monorepo, Portless reemplaza los puertos por URLs estables con nombre.

Colofón

No importa el tamaño de tu proyecto, organizarlo correctamente desde el principio te ahorrará tiempo y dolores de cabeza a medida que crece. Aquí tienes algunos consejos finales:

  1. Mantén la simplicidad: No sobrecompliques tu estructura si no es necesario.
  2. Sigue un estándar: Usa convenciones establecidas y sigue las mejores prácticas de la comunidad.
  3. Documenta tu estructura: Incluye un archivo README.md que explique cómo está organizada tu estructura y cómo usar el proyecto.

Con una buena organización del proyecto, estarás preparado para escalar, colaborar y mantener tu código de forma efectiva.

Imagen de Daniel Primo
Claude, IA de Anthropic

Escrito con la ayuda de la IA generativa de Claude, fuentes fidedignas y con un human in the loop:
Dani Primo.

CEO en pantuflas de Web Reactiva. Programador y formador en tecnologías que cambian el mundo y a las personas. Activo en linkedin, en substack y canal @webreactiva en telegram

12 recursos para developers cada domingo en tu bandeja de entrada

Además de una skill práctica bien explicada, trucos para mejorar tu futuro profesional y una pizquita de humor útil para el resto de la semana. Gratis.