-
Notifications
You must be signed in to change notification settings - Fork 0
Evaluación de Arquitectura
La arquitectura de nuestro sistema sigue un patrón de tres capas:
(1) Frontend en Next.js/React con Tailwind CSS,
(2) Backend monolítico en Express.js con gestión de rutas centralizadas, y
(3) Base de datos PostgreSQL containerizada. La comunicación es mediante API REST con autenticación JWT. Todo se orquesta con Docker Compose.
Flujo principal: Usuario interactúa en frontend -> Solicitud HTTP -> Procesamiento en Express -> Consulta a BD -> Respuesta JSON.
¿Qué es? La arquitectura actual presenta un alto acoplamiento entre capas y componentes. Todo el código backend está centralizado en un único archivo (index.js) que mezcla rutas HTTP, lógica de autenticación, validaciones, operaciones de base de datos y reglas de negocio. El frontend tiene componentes que llaman directamente a la API sin una capa de abstracción consistente.
¿Por qué es importante? Según el estudio ATAM de 15 años (Bellomo et al., 2015), Modifiability es el atributo de calidad más importante con 26% de ocurrencia. En específico, "reducir acoplamiento" es la preocupación principal (32%). En nuestro contexto, cambios en reglas de negocio impactan múltiples capas simultáneamente.
¿Cómo impacta decisiones arquitectónicas?
- Determina si se puede agregar nuevas funcionalidades sin modificar código existente (Principio Open/Closed).
- Define la capacidad de reuso de componentes.
- Afecta el costo de mantenimiento a largo plazo.
- Influye en cómo se organizan las responsabilidades entre frontend y backend.
| Fortalezas | Debilidades |
|---|---|
| - Separación física Frontend/Backend. - API REST como interfaz desacoplada. - Middleware de autenticación centralizado. - Funciones auxiliares facilitan cambios. |
- 14+ endpoints en un solo index.js.- Lógica de negocio y queries inline en handlers. - Sin capa de servicios definida. - Validaciones distribuidas y sin DTOs. |
Cambios necesarios para mejorar: Estructurar el backend en capas para aislar responsabilidades.
/backend
/routes (Express routes. Ej: app.post('/api/loans', createLoan))
/services (Lógica de negocio. Ej: calculateMonthlyPayment())
/models (Acceso a BD. Ej: getLoanById())
/middleware (Auth, validación)
/utils (Funciones auxiliares)
¿Qué es? El sistema actual no posee optimizaciones para garantizar bajos tiempos de latencia bajo carga concurrente. Las consultas a base de datos no tienen índices, no hay estrategia de caching, el frontend carga datos sin compresión y no hay limitación de payload.
¿Por qué es importante? Según ATAM Study 2, Performance es una de las mayores preocupaciones (16%). En un sistema de préstamos, el simulador de cuotas y búsquedas de solicitudes son consultas frecuentes que afectan directamente la experiencia del usuario.
¿Cómo impacta decisiones arquitectónicas?
- Define qué datos deben cachearse y por cuánto tiempo.
- Afecta la estructura de índices en la base de datos.
- Determina si se necesita una capa de caché distribuido (Redis).
- Influye en la estrategia de paginación y lazy loading en frontend.
| Fortalezas | Debilidades |
|---|---|
| - Next.js optimiza compilación (code splitting). - Express es ligero y eficiente para APIs. - PostgreSQL maneja queries complejas. |
- Sin índices en BD (queries lentas). - Sin compresión de respuestas JSON. - Sin caching (simulador recalcula siempre). - Sin paginación y sin lazy loading. |
Cambios necesarios para mejorar:
-- 1. Base de Datos: Agregar indices criticos
CREATE INDEX idx_users_rut ON users(rut);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_loans_user_rut ON loans(user_rut);
// 2. Backend: Habilitar compresion y cache
const compression = require('compression');
app.use(compression());
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 3600 }); // 1 hora
app.post('/api/loans/simulate', (req, res) => {
const cacheKey = `sim_${req.body.amount}_${req.body.term}`;
const cached = cache.get(cacheKey);
if (cached) return res.json(cached);
const result = calculateLoan(req.body);
cache.set(cacheKey, result);
res.json(result);
});
¿Qué es? Aunque el sistema usa Docker, la estrategia de despliegue es frágil. El esquema de BD se crea dinámicamente con DROP TABLE (destructivo), no hay versionado de migraciones, no existe CI/CD, y los cambios pueden ser incompatibles con versiones anteriores.
¿Por qué es importante? Deployability es un concern emergente en proyectos de TI (10% de ocurrencia). Un sistema frágil en despliegue genera deuda técnica, riesgo de downtime y un largo ciclo de testeo.
¿Cómo impacta decisiones arquitectónicas?
- Define si el despliegue es manual o automatizado.
- Afecta la posibilidad de rollback seguro ante fallos.
- Determina cómo se manejan cambios de esquema sin perder datos.
- Influye en la compatibilidad entre versiones de API y BD.
| Fortalezas | Debilidades |
|---|---|
| - Docker containeriza la aplicación. - docker-compose orquesta localmente. - Servicios independientes. |
- Migraciones destructivas (DROP TABLE).- Sin versionado de esquema. - Seed destructivo y no reproducible. - Sin pipeline de CI/CD automatizado. |
Cambios necesarios para mejorar:
// 1. Backend: Usar Knex.js para migraciones versionadas
exports.up = (knex) => {
return knex.schema.createTable('users', table => {
table.text('rut').primary();
table.text('email').notNullable().unique();
table.timestamps(true, true);
});
};
exports.down = (knex) => { return knex.schema.dropTable('users'); };
# 2. Pipeline: .github/workflows/deploy.yml
name: Deploy
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
run: npm test
- name: Build and Push Docker image
run: |
docker build -t myapp:${{ github.sha }} .
docker push myapp:${{ github.sha }}
¿Qué es? El sistema maneja datos financieros e identidad de usuarios, pero presenta vulnerabilidades en su configuración actual: gestión insegura del JWT_SECRET (dependencia frágil del process.env sin rotación), almacenamiento de password_hash que requiere revisión de algoritmos (bcrypt/argon2), y posible exposición estática del directorio de archivos (uploads).
¿Por qué es importante? En aplicaciones financieras, la confidencialidad y la integridad de los datos son críticas. Una brecha en el manejo del secreto JWT o la exposición de documentos subidos (rut, comprobantes) puede resultar en robo de identidad, pérdida de confianza y consecuencias legales.
| Fortalezas | Debilidades |
|---|---|
| - Uso de JWT para proteger sesiones state-less. - Middleware bloquea acceso no autenticado. - Hasheo de contraseñas implementado. |
- Secretos mal gestionados o hardcodeados. - Directorio de uploads potencialmente público. - Falta de rate-limiting (protección contra fuerza bruta). |
Cambios necesarios para mejorar:
Validar que JWT_SECRET tenga suficiente entropía, ocultar la carpeta de uploads detrás de un endpoint autenticado (o usar AWS S3), e implementar un limitador de peticiones (express-rate-limit) en el endpoint de login.
| Atributo | Trade-off (Beneficio vs. Costo) | Riesgo Asociado | Mitigacion |
|---|---|---|---|
| Modifiability | Refactorizar a capas exige tiempo y trabajo inicial, pero reduce el costo de cambios futuros y facilita pruebas. | Refactorización masiva puede introducir regresiones y retrasos en entrega si no se hace incrementalmente. | Hacer refactor incremental (strangler pattern), añadir pruebas unitarias y usar feature flags. |
| Performance | Añadir caching, índices y compresión mejora la latencia, pero añade complejidad operacional y riesgo de datos desactualizados. | Caché mal invalidado o índices pobres pueden producir resultados inconsistentes o queries lentas bajo carga. | Usar TTLs, invalidación explícita, instrumentar métricas y probar carga en índices críticos. |
| Deployability | Implementar migraciones y CI/CD aumenta el esfuerzo inicial, pero reduce riesgo en despliegues y permite rollbacks. | Las migraciones destructivas actuales (DROP TABLE) pueden causar pérdida de datos si se ejecutan en producción. |
Reemplazar DROP TABLE por migraciones versionadas, hacer backups automáticos y probar en staging. |
| Security | Centralizar secretos y restringir accesos añade complejidad a la infraestructura (configuración de variables, manejo de tokens), pero es vital para evitar brechas legales. | Mantener los secretos expuestos arriesga robo masivo de datos. Al aplicar protecciones como rate-limiting, se corre el riesgo de bloquear usuarios legítimos por error (falsos positivos). | Inyectar variables desde el pipeline CI/CD, configurar umbrales de rate-limit tolerantes al inicio y servir archivos sensibles solo a través de rutas privadas o AWS S3. |
La arquitectura actual cumple con los requisitos funcionales iniciales, logrando una correcta separación física entre el Frontend y el Backend. Sin embargo, la acumulación de deuda técnica en la capa del servidor (alto acoplamiento en index.js) y la ausencia de prácticas de despliegue seguro representan un riesgo significativo para la escalabilidad, el rendimiento bajo carga y el mantenimiento a largo plazo.
El diseño actual priorizó la velocidad de desarrollo inicial (Time-to-Market), pero requiere una evolución inmediata para soportar operaciones continuas y seguras.
Para mitigar los riesgos más críticos detectados en esta evaluación ATAM sin detener el ciclo de desarrollo, se proponen las siguientes 3 acciones de ejecución inmediata:
-
Migraciones y Backups (Deployability - Riesgo Critico): Reemplazar inmediatamente las funciones destructivas (
DROP TABLE) por un sistema de migraciones versionadas (ej. Knex.js). Configurar respaldos automatizados de PostgreSQL para garantizar la recuperación ante desastres. -
Indices Criticos (Performance - Quick Win): Ejecutar scripts SQL para crear índices en las columnas de mayor consulta (
rut,email,user_rut). Es un cambio de muy bajo esfuerzo que previene la degradación drástica de latencia al crecer los datos. -
Refactorizacion Incremental (Modifiability - Deuda Tecnica): No reescribir todo a la vez. Iniciar la separación de
index.jsaislando únicamente el dominio más complejo (préstamos) hacia un nuevo archivoLoanService.js. Esto establecerá el patrón de Arquitectura en Capas para que el equipo lo replique en futuras tareas.