Arquitectura Limpia (Clean Architecture)

2025-02-03

La Arquitectura Limpia (Clean Architecture) es un enfoque de diseño de software propuesto por Robert C. Martin que busca la independencia de los sistemas con respecto a frameworks, bases de datos y detalles de implementación. Su principal objetivo es lograr código mantenible, flexible y fácil de probar.

A través de la separación de responsabilidades, Clean Architecture permite que los sistemas sean adaptables a cambios tecnológicos sin alterar la lógica de negocio principal. En este artículo, exploraremos sus principios, beneficios, estructura y cómo aplicarla en proyectos de software modernos.

 

Historia de la Arquitectura Limpia

 

La Arquitectura Limpia fue propuesta por Robert C. Martin en 2012, consolidando una serie de principios y patrones de diseño que habían evolucionado a lo largo de los años en el desarrollo de software. Uncle Bob ya había trabajado en enfoques como SOLID y la arquitectura hexagonal, que influyeron directamente en este modelo arquitectónico.

Antes de la Arquitectura Limpia, muchas aplicaciones eran altamente dependientes de frameworks y bases de datos, lo que generaba problemas de mantenibilidad y escalabilidad. Con el tiempo, los desarrolladores comenzaron a darse cuenta de la importancia de separar la lógica de negocio de la infraestructura tecnológica, lo que llevó a la formalización de Clean Architecture como un estándar de buenas prácticas en el diseño de software.

Clean Architecture se basa en conceptos previos como la Arquitectura en Capas, la Arquitectura Hexagonal (Ports and Adapters) de Alistair Cockburn y la Arquitectura en Cebolla (Onion Architecture) de Jeffrey Palermo, pero con un enfoque más claro en la independencia de los componentes y la testabilidad del código.

 

Principios Fundamentales de Clean Architecture

 

La Arquitectura Limpia sigue varios principios clave que garantizan la modularidad y la independencia de los componentes del software:

 

1. Independencia de Frameworks

  • La aplicación no debe depender de frameworks específicos.
  • Los frameworks son herramientas, no dictan la arquitectura.

 

2. Independencia de la Interfaz de Usuario

  • La UI puede cambiar sin afectar la lógica de negocio.
  • Se pueden crear diferentes interfaces (web, móvil, CLI) sin alterar el núcleo.

 

3. Independencia de la Base de Datos

  • Se puede cambiar la base de datos sin modificar la lógica de negocio.
  • La lógica no debe depender de detalles de almacenamiento.

 

4. Independencia de Detalles Externos

  • La lógica de negocio es agnóstica a frameworks, bibliotecas o herramientas externas.

 

Estructura de Clean Architecture

La Arquitectura Limpia se estructura en capas concéntricas que reflejan niveles de dependencia y responsabilidad. Este diseño no es arbitrario: busca garantizar independencia tecnológicaalta mantenibilidad y fácil testeo del sistema.

Cada capa tiene un conjunto claro de responsabilidades y establece una regla de oro:
Las dependencias solo pueden ir hacia adentro, es decir, hacia el núcleo de la aplicación.

1. Entidades (Core / Enterprise Business Rules)

Qué son: Son el corazón del sistema. Representan las reglas de negocio más generales y estables a largo plazo.
Por qué existen: Al estar desacopladas de la tecnología, estas entidades pueden sobrevivir a múltiples generaciones de frameworks o bases de datos sin cambios.
Ejemplos: Modelos de dominio, validaciones fundamentales del negocio.

 

2. Casos de Uso (Application Business Rules)

Qué son: Definen la lógica específica de la aplicación: qué se puede hacer y en qué condiciones. Orquestan el comportamiento del sistema.
Por qué existen: Encapsulan los procesos del negocio de forma que pueden ser reutilizados o adaptados sin tocar ni las entidades ni los detalles técnicos.
Ejemplos: Registrar un usuario, procesar una compra, generar un informe.

 

3. Adaptadores de Interfaz (Interface Adapters)

Qué son: Actúan como traductores entre el mundo interno de la aplicación (casos de uso y entidades) y el mundo externo (UI, DB, APIs).
Por qué existen: Permiten que el núcleo del sistema no dependa de cómo se presentan o persisten los datos.
Ejemplos: Controladores REST, repositorios, DTOs, mappers, presentadores.

 

4. Infraestructura y Frameworks (Frameworks & Drivers)

 

Qué son: Herramientas externas que proporcionan funcionalidad específica (como persistencia o comunicación).
Por qué están en el borde: Son los elementos más propensos a cambios tecnológicos y deben ser fácilmente reemplazables sin afectar el núcleo del sistema.
Ejemplos: Bases de datos, frameworks web, librerías de UI, servicios externos.

 

¿Por qué esta organización importa?

 

  • Aislamiento del dominio: La lógica de negocio no depende de detalles técnicos, lo que permite evolucionarla de forma independiente.
  • Testabilidad: Puedes probar entidades y casos de uso sin necesidad de base de datos ni interfaz gráfica.
  • Sustentabilidad tecnológica: Permite reemplazar o actualizar herramientas externas sin rehacer el sistema completo.
  • Alta cohesión, bajo acoplamiento: Cada capa tiene un foco claro y depende solo de capas más internas.

 

Beneficios de Clean Architecture

 

Alta mantenibilidad: Separación clara de responsabilidades facilita cambios sin afectar todo el sistema.

Facilidad para pruebas: Permite testear la lógica de negocio sin depender de la infraestructura.

Escalabilidad: Permite agregar nuevas funcionalidades sin afectar capas internas.

Flexibilidad tecnológica: Se puede cambiar la base de datos o framework sin alterar la lógica de negocio.

 

Organización del Proyecto en Clean Architecture

 

La Arquitectura Limpia afecta directamente la forma en que se estructura y organiza un proyecto de software. Siguiendo sus principios, se recomienda dividir el código en capas bien definidas para garantizar la separación de responsabilidades y la independencia de los componentes. A continuación, explicamos cómo organizar un proyecto bajo este modelo:

 

Separación en Capas

El código debe dividirse en capas bien estructuradas, cada una con una responsabilidad específica:

  • Capa de Entidades (Entities): Contiene las reglas de negocio más generales y reutilizables.
  • Capa de Casos de Uso (Use Cases): Define la lógica de aplicación y las interacciones entre las entidades.
  • Capa de Adaptadores de Interfaz (Interface Adapters): Maneja la conversión de datos entre la aplicación y las entradas/salidas externas.
  • Capa de Infraestructura (Infrastructure & Frameworks): Incluye bases de datos, frameworks y bibliotecas externas.

 

Organización de Archivos y Carpetas

 

Se recomienda una estructura de archivos clara y modular para aplicar Clean Architecture en un proyecto real, podemos seguir una estructura de carpetas organizada por capas:

/ src
  / core
    / entities
    / usecases
  / infrastructure
    / database
    / frameworks
  / application
    / controllers
    / gateways
  / ui
    / views
    / api

 

Implementación de Clean Architecture en Node.js

 

A continuación, presentamos una implementación sencilla de Clean Architecture en Node.js con una estructura de archivos bien definida.

 

Estructura del Proyecto
/ src
  / core
    / entities
      user.ts
    / usecases
      getUser.ts
  / infrastructure
    / database
      userRepository.ts
  / application
    / controllers
      userController.ts
    / gateways
      userGateway.ts
  / ui
    / routes
      userRoutes.ts
  server.ts

 

1. Definiendo la Entidad (Entity)

Archivo: src/core/entities/user.ts

export class User {
  constructor(public id: number, public name: string) {}
}

 

2. Creando el Caso de Uso (Use Case)

Archivo: src/core/usecases/getUser.ts

import { User } from "../entities/user";
import { UserRepository } from "../../infrastructure/database/userRepository";

export class GetUserUseCase {
  constructor(private userRepository: UserRepository) {}
  
  execute(userId: number): User {
    return this.userRepository.findById(userId);
  }
}

 

3. Implementando el Repositorio (Infrastructure Layer)

Archivo: src/infrastructure/database/userRepository.ts

import { User } from "../../core/entities/user";

export class UserRepository {
  private users = [new User(1, "John Doe"), new User(2, "Jane Doe")];

  findById(id: number): User {
    return this.users.find(user => user.id === id) || new User(0, "Not Found");
  }
}

 

4. Creando el Controlador (Application Layer)

Archivo: src/application/controllers/userController.ts

import { GetUserUseCase } from "../../core/usecases/getUser";
import { UserRepository } from "../../infrastructure/database/userRepository";

export class UserController {
  private getUserUseCase = new GetUserUseCase(new UserRepository());

  getUser(req, res) {
    const userId = parseInt(req.params.id, 10);
    const user = this.getUserUseCase.execute(userId);
    res.json(user);
  }
}

 

5. Configurando las Rutas (UI Layer)

Archivo: src/ui/routes/userRoutes.ts

import express from "express";
import { UserController } from "../../application/controllers/userController";

const router = express.Router();
const userController = new UserController();

router.get("/user/:id", (req, res) => userController.getUser(req, res));

export default router;

 

6. Configurando el Servidor

Archivo: server.ts

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

const app = express();

app.use(express.json());
app.use(userRoutes);

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

 

Con esta implementación sencilla de Clean Architecture en Node.js, logramos modularidad y separación de responsabilidades, permitiendo un mantenimiento y escalabilidad eficientes.

Whatsapp Mentores Tech