El desarrollo de grandes aplicaciones en TypeScript requiere una estructura de proyecto bien definida para mantener la escalabilidad, mantenibilidad y eficiencia del código. Una mala organización puede llevar a problemas de acoplamiento, dificultades en la depuración y una curva de aprendizaje elevada para nuevos desarrolladores en el equipo.

En este artículo, exploraremos cómo organizar un proyecto grande en TypeScript utilizando buenas prácticas, patrones de diseño y herramientas avanzadas.

1. Configuración Inicial del Proyecto Link to heading

Antes de estructurar el código, debemos preparar un entorno robusto en TypeScript.

1.1 Crear un Proyecto TypeScript Link to heading

Ejecuta el siguiente comando para inicializar un nuevo proyecto:

mkdir my-large-ts-project && cd my-large-ts-project
npm init -y
npm install typescript --save-dev

1.2 Configuración de tsconfig.json Link to heading

Generamos un archivo de configuración:

npx tsc --init

Modificamos el archivo tsconfig.json con las siguientes opciones recomendadas:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

2. Estructura de Carpetas para Grandes Proyectos Link to heading

Una estructura bien definida facilita la escalabilidad del proyecto. Un modelo recomendado es:

/my-large-ts-project
│── /src
│   ├── /config
│   │   ├── appConfig.ts
│   │   ├── dbConfig.ts
│   ├── /models
│   │   ├── userModel.ts
│   │   ├── orderModel.ts
│   ├── /services
│   │   ├── userService.ts
│   │   ├── orderService.ts
│   ├── /controllers
│   │   ├── userController.ts
│   │   ├── orderController.ts
│   ├── /routes
│   │   ├── userRoutes.ts
│   │   ├── orderRoutes.ts
│   ├── /middlewares
│   │   ├── authMiddleware.ts
│   ├── /utils
│   │   ├── logger.ts
│   ├── /tests
│   │   ├── userService.test.ts
│   ├── app.ts
│── /dist
│── package.json
│── tsconfig.json
│── README.md

Descripción de las Carpetas Link to heading

  • /config: Configuración de base de datos, variables de entorno y opciones globales.
  • /models: Definiciones de modelos y entidades.
  • /services: Lógica de negocio (capa intermedia entre controladores y base de datos).
  • /controllers: Manejo de solicitudes HTTP.
  • /routes: Definición de rutas para cada entidad.
  • /middlewares: Middleware para autenticación, validaciones, etc.
  • /utils: Funciones auxiliares reutilizables.
  • /tests: Pruebas unitarias y de integración.

3. Modularización y Separación de Responsabilidades Link to heading

Dividir el código en módulos independientes mejora la reutilización y el mantenimiento.

Ejemplo: Modelo de Usuario (models/userModel.ts) Link to heading

export interface User {
  id: string;
  name: string;
  email: string;
}

Ejemplo: Servicio de Usuario (services/userService.ts) Link to heading

import { User } from "../models/userModel";

const users: User[] = [];

export class UserService {
  static getAllUsers(): User[] {
    return users;
  }

  static addUser(user: User): void {
    users.push(user);
  }
}

Ejemplo: Controlador de Usuario (controllers/userController.ts) Link to heading

import { Request, Response } from "express";
import { UserService } from "../services/userService";

export const getUsers = (req: Request, res: Response) => {
  res.json(UserService.getAllUsers());
};

export const addUser = (req: Request, res: Response) => {
  const user = req.body;
  UserService.addUser(user);
  res.status(201).json(user);
};

4. Uso de TypeScript con Express.js Link to heading

4.1 Instalar Express y Tipos Link to heading

npm install express
npm install @types/express --save-dev

4.2 Definir las Rutas (routes/userRoutes.ts) Link to heading

import { Router } from "express";
import { getUsers, addUser } from "../controllers/userController";

const router = Router();

router.get("/users", getUsers);
router.post("/users", addUser);

export default router;

4.3 Configurar el Servidor (app.ts) Link to heading

import express from "express";
import userRoutes from "./routes/userRoutes";

const app = express();
app.use(express.json());
app.use("/api", userRoutes);

app.listen(3000, () => console.log("Servidor corriendo en http://localhost:3000"));

5. Implementación de Pruebas con Jest Link to heading

Las pruebas garantizan la estabilidad del código en proyectos grandes.

5.1 Instalar Jest Link to heading

npm install --save-dev jest ts-jest @types/jest

5.2 Configurar Jest en package.json Link to heading

"scripts": {
  "test": "jest --config jest.config.js"
}

5.3 Ejemplo de Prueba (tests/userService.test.ts) Link to heading

import { UserService } from "../services/userService";

test("Agregar un usuario", () => {
  UserService.addUser({ id: "1", name: "Juan", email: "juan@example.com" });
  expect(UserService.getAllUsers().length).toBe(1);
});

Conclusión Link to heading

Estructurar grandes proyectos en TypeScript requiere una organización clara y modular. Al seguir estas prácticas, logramos:

Código mantenible y escalable. ✅ Separación clara de responsabilidades. ✅ Reutilización y modularidad del código. ✅ Facilidad para pruebas unitarias e integración.