Volver al inicio
Backend

Backend - Arquitectura

Conceptos fundamentales de arquitectura backend: Node puro vs frameworks, Clean Architecture, monolitos vs microservicios, comunicación entre servicios y API Gateway

ConceptosBest Practices

Introducción

La arquitectura es fundamental para construir aplicaciones backend escalables y mantenibles. Estos conceptos te ayudarán a tomar decisiones arquitectónicas informadas.

Node Puro vs NestJS

1 preguntas

1

Node puro vs NestJS: ¿Cuándo usar cada uno?

Comparación rápida:
AspectoNode.js PuroNestJS
Complejidad
✅ Simple
❌ Más complejo
Estructura
❌ Tú decides
✅ Estructura definida
TypeScript
⚠️ Opcional
✅ Integrado
Dependency Injection
❌ Manual
✅ Built-in
Decoradores
❌ No
✅ Sí
Testing
⚠️ Manual setup
✅ Integrado
Curva de aprendizaje
✅ Baja
❌ Media-Alta
Flexibilidad
✅ Total
⚠️ Limitada por framework
Node.js Puro - Usa cuando:
✓Proyectos pequeños/medianos: APIs simples, scripts
✓Máxima flexibilidad: Necesitas control total
✓Equipo pequeño: Sin necesidad de estructura compleja
✓Prototipado rápido: Desarrollo rápido sin overhead
✓Microservicios simples: Servicios pequeños y específicos
Ejemplo Node puro:
javascript
const express = require('express');
const app = express();

app.get('/users', async (req, res) => {
  const users = await db.getUsers();
  res.json(users);
});

app.listen(3000);
NestJS - Usa cuando:
✓Proyectos grandes: Aplicaciones enterprise
✓Equipo grande: Necesitas estructura consistente
✓TypeScript: Quieres type safety
✓Arquitectura definida: Clean Architecture, SOLID
✓Testing: Necesitas testing robusto
✓Escalabilidad: Proyecto que crecerá
Ejemplo NestJS:
typescript
@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}
  
  @Get()
  findAll(): Promise<User[]> {
    return this.usersService.findAll();
  }
}
Recomendación:
•Node puro: Para proyectos pequeños o cuando necesitas flexibilidad
•NestJS: Para proyectos enterprise o cuando trabajas en equipo grande

Clean Architecture

1 preguntas

1

¿Qué es Clean Architecture?

Definición: Arquitectura que separa el código en capas independientes, donde las capas internas no dependen de las externas.
Principio clave: Dependencias apuntan hacia adentro (hacia el dominio).
Capas (de afuera hacia adentro):
1.Frameworks & Drivers (Externa):
• Web framework (Express, NestJS)
• Base de datos
• APIs externas
• UI
2.Interface Adapters:
• Controllers
• Presenters
• Gateways
• DTOs
3.Use Cases (Aplicación):
• Lógica de negocio específica
• Casos de uso
• Orquestación
4.Entities (Dominio):
• Entidades de negocio
• Reglas de dominio
• Lógica core
Ejemplo de estructura:
src/
  domain/          # Entidades puras
    user.ts
  use-cases/       # Lógica de aplicación
    create-user.ts
  adapters/        # Interfaces
    controllers/
    repositories/
  infrastructure/  # Frameworks
    database/
    http/
Ventajas:
✓Testabilidad: Fácil testear lógica de negocio
✓Independencia: Cambiar frameworks sin afectar dominio
✓Mantenibilidad: Código organizado y claro
✓Escalabilidad: Fácil agregar nuevas features
Desventajas:
✗ Más código inicial
✗ Curva de aprendizaje
✗ Puede ser overkill para proyectos pequeños
Regla de dependencia:
•Capas externas pueden usar internas
•Capas internas NUNCA usan externas
•Comunicación entre capas vía interfaces

Monolito vs Microservicios

4 preguntas

1

Monolito vs Microservicios: ¿Cuándo usar cada uno?

Comparación:
AspectoMonolitoMicroservicios
Complejidad
✅ Baja
❌ Alta
Deployment
✅ Simple (uno)
❌ Complejo (múltiples)
Escalabilidad
⚠️ Vertical
✅ Horizontal
Desarrollo
✅ Rápido inicial
❌ Más lento
Testing
✅ Más simple
❌ Más complejo
Debugging
✅ Más fácil
❌ Más difícil
Tecnología
⚠️ Una stack
✅ Múltiples stacks
Falla aislada
❌ Todo cae
✅ Solo un servicio
Monolito - Usa cuando:
✓Proyecto pequeño/mediano: Equipo pequeño
✓Desarrollo inicial: MVP, prototipo
✓Lógica acoplada: Dominio muy relacionado
✓Equipo pequeño: < 10 desarrolladores
✓Deployment simple: Una aplicación
Ventajas del Monolito:
•Desarrollo más rápido
•Testing más simple
•Debugging más fácil
•Menos overhead operacional
•Transacciones ACID más fáciles
Microservicios - Usa cuando:
✓Proyecto grande: Equipo grande (> 20 devs)
✓Escalabilidad: Necesitas escalar partes específicas
✓Dominios separados: Bounded contexts claros
✓Tecnologías diferentes: Necesitas diferentes stacks
✓Equipos independientes: Equipos por servicio
Ventajas de Microservicios:
•Escalabilidad independiente
•Tecnología por servicio
•Despliegue independiente
•Falla aislada
•Equipos autónomos
Desventajas de Microservicios:
✗ Complejidad operacional
✗ Latencia de red
✗ Transacciones distribuidas complejas
✗ Testing más difícil
✗ Debugging distribuido
Recomendación:
•Empezar con monolito
•Migrar a microservicios cuando sea necesario
•No usar microservicios "porque sí"
2

Microservicios: pros y contras

Ventajas (Pros):
✓Escalabilidad independiente:
• Escalar solo servicios que necesitan
• Optimizar recursos por servicio
✓Tecnología flexible:
• Cada servicio puede usar diferente stack
• Elegir mejor herramienta para cada caso
✓Despliegue independiente:
• Deployar un servicio sin afectar otros
• Rollback granular
✓Equipos autónomos:
• Equipos pueden trabajar independientemente
• Ownership claro por servicio
✓Falla aislada:
• Si un servicio falla, otros siguen funcionando
• Mejor resiliencia
✓Desarrollo paralelo:
• Múltiples equipos trabajando simultáneamente
• Menos conflictos de código
Desventajas (Contras):
✗ Complejidad operacional:
• Múltiples servicios para monitorear
• Más infraestructura
• DevOps más complejo
✗ Latencia de red:
• Comunicación entre servicios añade latencia
• Múltiples llamadas de red
✗ Transacciones distribuidas:
• ACID difícil de mantener
• Necesitas Saga pattern o eventual consistency
✗ Testing más complejo:
• Testing de integración más difícil
• Necesitas mocks/stubs
• Testing end-to-end complejo
✗ Debugging distribuido:
• Trazar requests entre servicios
• Logs distribuidos
• Necesitas distributed tracing
✗ Overhead de desarrollo:
• Más código boilerplate
• Configuración por servicio
• Más tiempo de setup
✗ Consistencia de datos:
• Eventual consistency
• Sincronización compleja
Cuándo vale la pena:
✓Equipo grande (> 20 personas)
✓Dominios claramente separados
✓Necesitas escalar partes específicas
✓Equipos independientes
✓Infraestructura madura
3

¿Cómo manejar comunicación entre servicios?

Patrones de comunicación:
1. Síncrona (Request/Response):
✓HTTP/REST:
javascript
// Servicio A llama a Servicio B
const response = await fetch('http://service-b/api/users');
const users = await response.json();
✓gRPC:
javascript
// Más eficiente que REST
const client = new UserServiceClient('service-b:50051');
const users = await client.getUsers({});
Ventajas:
•Simple de implementar
•Respuesta inmediata
•Fácil de debuggear
Desventajas:
•Acoplamiento temporal
•Si servicio B cae, A también falla
•Latencia acumulada
2. Asíncrona (Event-Driven):
✓Message Queue (RabbitMQ, Kafka):
javascript
// Servicio A publica evento
await channel.publish('user.created', { userId: 123 });

// Servicio B consume evento
channel.consume('user.created', (msg) => {
  // Procesar evento
});
✓Event Bus (Redis Pub/Sub):
javascript
// Publicar
redis.publish('events', JSON.stringify(event));

// Suscribir
redis.subscribe('events', (channel, message) => {
  // Procesar evento
});
Ventajas:
•Desacoplamiento
•Resiliencia (si B cae, eventos se acumulan)
•Escalabilidad
•No bloquea
Desventajas:
•Más complejo
•Eventual consistency
•Debugging más difícil
3. Híbrida:
✓Síncrona para datos críticos:
• Operaciones que necesitan respuesta inmediata
• Validaciones
✓Asíncrona para procesos:
• Notificaciones
• Procesamiento en background
• Eventos de dominio
Mejores prácticas:
✓Circuit Breaker:
javascript
// Prevenir cascading failures
if (circuitBreaker.isOpen()) {
  return fallbackResponse();
}
✓Retry con backoff:
javascript
// Reintentar con delay exponencial
await retry(apiCall, { retries: 3, backoff: 'exponential' });
✓Timeout:
javascript
// No esperar indefinidamente
const response = await fetch(url, { timeout: 5000 });
✓Idempotencia:
javascript
// Requests pueden duplicarse
if (alreadyProcessed(requestId)) {
  return cachedResponse;
}
Cuándo usar cada una:
EscenarioPatrónRazón
Datos críticos
Síncrono
Necesitas respuesta inmediata
Notificaciones
Asíncrono
No bloquea, eventual consistency OK
Procesamiento pesado
Asíncrono
No bloquea otros servicios
Consulta simple
Síncrono
Simple y directo
Eventos de dominio
Asíncrono
Desacoplamiento
4

¿Qué es un API Gateway?

Definición: Punto de entrada único que actúa como intermediario entre clientes y microservicios.
Funciones principales:
✓Routing:
Cliente → API Gateway → Microservicio A
                      → Microservicio B
                      → Microservicio C
✓Autenticación/Autorización:
javascript
// Gateway valida token antes de routing
if (!isValidToken(req.headers.authorization)) {
  return res.status(401).json({ error: 'Unauthorized' });
}
✓Rate Limiting:
javascript
// Limitar requests por cliente
if (rateLimitExceeded(clientId)) {
  return res.status(429).json({ error: 'Too many requests' });
}
✓Load Balancing:
javascript
// Distribuir carga entre instancias
const service = loadBalancer.selectInstance('user-service');
✓Transformación de requests:
javascript
// Adaptar formato de request
const adaptedRequest = transformRequest(req);
✓Caching:
javascript
// Cachear respuestas frecuentes
if (cached = cache.get(key)) {
  return cached;
}
✓Logging y Monitoreo:
javascript
// Centralizar logs
logger.log({ service: 'user-service', duration: 150ms });
Arquitectura:
[Clientes]
    ↓
[API Gateway]
    ↓
[Microservicios]
Ventajas:
✓Punto único de entrada: Cliente solo conoce gateway
✓Seguridad centralizada: Auth en un lugar
✓Simplifica cliente: No necesita conocer múltiples endpoints
✓Cross-cutting concerns: Rate limiting, logging, etc.
✓Versionado: Manejar versiones de API
Desventajas:
✗ Single point of failure: Si gateway cae, todo cae
✗ Latencia adicional: Una capa más
✗ Complejidad: Otro componente para mantener
Implementaciones comunes:
•Kong: Open source, plugin-based
•AWS API Gateway: Managed service
•Nginx: Reverse proxy + gateway
•Zuul: Netflix (Java)
•Express Gateway: Node.js
Ejemplo básico:
javascript
// API Gateway
app.use('/api/users', proxy('http://user-service:3001'));
app.use('/api/orders', proxy('http://order-service:3002'));
app.use('/api/payments', proxy('http://payment-service:3003'));

// Cliente solo conoce
GET /api/users/123
// Gateway routea a user-service internamente
Cuándo usar:
✓Múltiples microservicios
✓Necesitas centralizar auth/rate limiting
✓Quieres simplificar cliente
✓Necesitas transformación de requests
✓Múltiples protocolos (HTTP, gRPC, WebSocket)