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.

Aquí hay algo que podría hacer cambiar tu futuro.

Usamos cookies de terceros para mostrar este iframe (que no es de publicidad ;).

Leer más

¿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.

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.

Escrito por:

Imagen de Daniel Primo

Daniel 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.