Skip to main content

Principios de Clean Architecture

Clean Architecture (Arquitectura Limpia) es un estilo arquitectónico propuesto por Robert C. Martin ("Uncle Bob"), que busca separar de forma estricta la lógica de negocio (núcleo) de los detalles de implementación como frameworks, bases de datos, controladores, interfaces o UI. El objetivo es crear software independiente, mantenible, testable y adaptable, sin depender de frameworks, bases de datos o dispositivos externos.

Capas de Clean Architecture

CapaPropósito principal
Entities (Dominio)Contiene reglas de negocio centrales, independientes del sistema.
Use Cases (Aplicación)Orquesta entidades para cumplir reglas específicas de negocio.
Interface AdaptersConvierte datos entre formatos externos y el modelo interno.
Frameworks & DriversBases de datos, frameworks (Angular/NestJS), UI, herramientas externas.

Principio de dependencia (Dependency Rule)

info

"El código de capas externas puede depender de capas internas, pero nunca al revés."

Esto significa que:

  • Entidades no conocen nada de controladores o base de datos.
  • Casos de uso usan interfaces (puertos) para acceder a repositorios o servicios externos.
  • La dirección de las dependencias siempre apunta hacia el dominio.

Ejemplo de uso NestJS

src/
├── domain/
│ ├── entities/
│ │ └── student.entity.ts
│ └── repositories/
│ └── student.repository.ts

├── application/
│ ├── use-cases/
│ │ ├── create-student.usecase.ts
│ │ └── list-students.usecase.ts
│ └── services.module.ts

├── infrastructure/
│ └── persistence/
│ └── in-memory-student.repository.ts

├── interfaces/
│ └── rest/
│ └── student.controller.ts

└── main.ts

Esta capa contiene el corazón de la aplicación, es decir, las reglas y estructuras fundamentales del negocio. No depende de ningún framework ni librería externa.

domain/entities/student.entity.ts
export class Student {
constructor(
public readonly id: string,
public name: string,
public email: string
) {}
}

La entidad Student se encarga de representar a un estudiante con sus datos esenciales (id, name, email), además, define el modelo de negocio: lo que significa ser un "estudiantes". Esta entidad es utilizada por los casos de uso para crear y manipular estudiantes, y es persistida por los repositorios.

domain/repositories/student.repository.ts
import { Student } from '../entities/student.entity';

export interface StudentRepository {
save(student: Student): Promise<void>;
findAll(): Promise<Student[]>;
}

StudentRepository es una interfaz que define lo que debe hacer un repositorio de estudiantes, sin decir cómo. Funciona como un contrato (puerto) para persistencia, el cual es usado por los casos de uso, e implementado por la capa de infraestructura.

Ejemplo de uso Angular v17+

src/
├── app/
│ ├── domain/
│ │ ├── models/
│ │ │ └── student.model.ts
│ │ └── repositories/
│ │ └── student.repository.ts
│ │
│ ├── application/
│ │ └── use-cases/
│ │ ├── add-student.usecase.ts
│ │ └── get-students.usecase.ts
│ │
│ ├── infrastructure/
│ │ └── http/
│ │ ├── student-http.service.ts
│ │ └── student.dto.ts
│ │
│ ├── presentation/
│ │ └── pages/
│ │ └── student-page/
│ │ ├── student-page.component.ts
│ │ └── student-page.component.html
│ │
│ └── app.config.ts

Esta capa define qué es el sistema, pero no cómo funciona técnicamente. No depende de Angular ni HTTP.

src/app/domain/models/student.model.ts
export interface Student {
id: string;
name: string;
email: string;
}

Student define la entidad estudiante. Es el tipo de dato que circula por todas las capas (modelo del negocio).

src/app/domain/repositories/student.repository.ts
import { Student } from '../models/student.model';

export abstract class StudentRepository {
abstract getAll(): Promise<Student[]>;
abstract create(name: string, email: string): Promise<Student>;
}

Por otro lado, student.repository.ts define un contrato abstracto (interfaz) para acceder a datos de estudiantes. No indica cómo se conectará al backend (eso lo resuelve la infraestructura). El contrato es usado por los casos de uso, y es implementada por la infraestructura.

Diagrama de secuencias

Buenas prácticas

PrincipioAplicación en Clean Architecture
Separación de responsabilidadesCada capa tiene un rol bien definido
Inversión de dependenciasRepositorios definidos como interfaces
Independencia tecnológicaEl dominio no depende de NestJS o Angular
ReutilizaciónCasos de uso reutilizables desde REST o GraphQL
TestabilidadCasos de uso y entidades fácilmente testeables

Referencias