Santiant
Node.js Arquitectura Clean Architecture Backend

Clean Architecture en aplicaciones de negocio con Node.js

Cómo aplicar los principios de Clean Architecture en aplicaciones Node.js que manejan lógica de negocio real, con ejemplos prácticos.

S
Santiago Moreno Arce
·

Por qué la arquitectura importa en aplicaciones de negocio

Las aplicaciones que dan soporte a operaciones de negocio reales tienen características que las distinguen de los proyectos pequeños:

  • La lógica de negocio es compleja y cambia con frecuencia.
  • Hay múltiples integraciones con sistemas externos.
  • El equipo crece y varias personas trabajan en el mismo código.
  • Los errores tienen impacto real en las operaciones.

En este contexto, una arquitectura bien definida no es un lujo, es una necesidad.

Los principios de Clean Architecture aplicados a Node.js

Clean Architecture, popularizada por Robert C. Martin, propone organizar el código en capas con dependencias que siempre apuntan hacia el centro:

Frameworks & Drivers (Express, TypeORM, Redis...)

Interface Adapters (Controllers, Presenters, Gateways)

Application Business Rules (Use Cases)

Enterprise Business Rules (Entities, Domain)

La regla fundamental: el dominio no depende de nada externo.

Estructura de directorios

Una estructura que funciona bien en proyectos Node.js reales:

src/
  domain/
    entities/
    repositories/      # interfaces, no implementaciones
    services/          # domain services
  application/
    use-cases/
    ports/             # interfaces de entrada y salida
  infrastructure/
    persistence/       # implementaciones de repositorios
    http/              # Express controllers
    external/          # clientes de APIs externas
  shared/
    errors/
    types/

Ejemplo: un caso de uso de pedido

// domain/entities/Order.ts
export class Order {
  constructor(
    public readonly id: string,
    public readonly customerId: string,
    public readonly items: OrderItem[],
    public status: OrderStatus
  ) {}

  confirm(): void {
    if (this.status !== OrderStatus.DRAFT) {
      throw new Error('Only draft orders can be confirmed');
    }
    this.status = OrderStatus.CONFIRMED;
  }
}

// application/use-cases/ConfirmOrder.ts
export class ConfirmOrderUseCase {
  constructor(
    private readonly orderRepository: OrderRepository,
    private readonly eventBus: EventBus
  ) {}

  async execute(orderId: string): Promise<void> {
    const order = await this.orderRepository.findById(orderId);
    if (!order) throw new OrderNotFoundError(orderId);

    order.confirm();

    await this.orderRepository.save(order);
    await this.eventBus.publish(new OrderConfirmedEvent(order));
  }
}

Lo que más cuesta implementar bien

La separación entre dominio e infraestructura

Es fácil filtrar dependencias de infraestructura al dominio. Un import de un ORM en una entidad parece inofensivo hasta que el dominio está acoplado a la base de datos y los tests se vuelven imposibles.

La granularidad de los casos de uso

¿Un caso de uso por operación o por flujo? En la práctica, un caso de uso por operación de negocio funciona mejor. Es más fácil de testear y de mantener.

La gestión de errores

Los errores de dominio (lógica de negocio) son distintos de los errores de infraestructura (red, base de datos). Mezclarlos genera código difícil de manejar.

Cuándo no aplicar Clean Architecture

No todo proyecto necesita esta arquitectura. Para scripts, utilidades o aplicaciones pequeñas con poca lógica de negocio, el overhead no vale la pena.

Clean Architecture tiene sentido cuando:

  • La lógica de negocio es el core del sistema.
  • El sistema va a crecer y a ser mantenido por un equipo.
  • La testeabilidad es importante.

Conclusión

Clean Architecture en Node.js no es un patrón rígido que hay que seguir al pie de la letra. Es un conjunto de principios que ayudan a mantener la lógica de negocio limpia y desacoplada de los detalles de implementación.

La clave es entender por qué cada capa existe y qué problema resuelve, no seguir la estructura mecánicamente.