Arquitectura Backend — API Modular por Dominio
El backend de Finnova es una API REST monolítica modular construida en Node.js + Express (TypeScript), desplegada en contenedores Docker sobre AWS. Es un monolito en cuanto a despliegue (un solo proceso/servicio), pero modular por dominio internamente: cada módulo de negocio es independiente y aislado, lo que permite extraerlo a un servicio separado más adelante si el dominio lo justifica.
Para la vista de infraestructura y despliegue completa, ver el Manual de arquitectura.
Stack del backend
| Capa | Tecnología | Rol |
|---|---|---|
| Runtime | Node.js | Entorno de ejecución |
| Framework HTTP | Express.js | Routing y middleware |
| Lenguaje | TypeScript (strict: true) | Tipado estático |
| Base de datos | PostgreSQL | Almacén relacional principal (+ réplica de lectura) |
| Object storage | AWS S3 | Contenido de cursos (video/material) |
| Reverse proxy | Nginx | TLS termination interno y proxy a la app |
| Contenedores | Docker | Empaquetado y despliegue |
| Infraestructura | AWS EC2 + Load Balancer | Cómputo y balanceo HTTPS |
| Pagos | Stripe | Procesamiento de suscripciones (PCI-DSS delegado) |
Vista de despliegue (infraestructura)
Elementos clave
- Load Balancer (HTTPS): único punto de entrada; termina TLS y enruta al servidor de aplicación.
- API Gateway: punto de entrada interno de la API; valida el JWT y delega a los módulos de dominio (ver Control de Sesiones).
- PostgreSQL primario + réplica de lectura: el primario absorbe escrituras; la réplica es read-only y respalda lecturas pesadas y backups automatizados.
- AWS S3: almacena el contenido de cursos (video/material); la base relacional solo guarda metadatos.
- Stripe: procesa suscripciones y notifica cambios vía webhooks (ver Stripe — Suscripciones).
Módulos de dominio
La API se divide en módulos de negocio independientes, orquestados por el API Gateway:
| Módulo | Responsabilidad | Requisitos asociados (ejemplos) |
|---|---|---|
| Auth | Registro, login (correo/Google/Apple), JWT, gestión de cuenta | RF01–RF11 |
| Finance Data Collection (FDC) | Conexión de cuentas bancarias, ingresos/gastos, patrimonio y deuda, dashboard | RF12–RF23 |
| Investments | Recomendaciones IA, inversiones populares, broker/Finnova | RF24–RF31 |
| Subscription | Modelos de suscripción, métodos de pago, facturación, renovaciones | RF32–RF46 |
| Courses | Catálogo de cursos, reproducción, progreso, certificados | RF47–RF62 |
| Accounting | Declaración anual, análisis de facturas | RF63–RF66 |
| Rewards / Leaderboard | Generación y canje de recompensas, ranking | RF67–RF69 |
El módulo Subscription es el único que se comunica con un servicio externo (Stripe). El módulo Courses es el único que lee de S3 además de PostgreSQL.
Estructura interna de un módulo
Cada módulo dentro de src/modules/ aplica una arquitectura en capas (controller → service → repository), separando transporte HTTP, lógica de negocio y acceso a datos:
src/
├── modules/
│ └── <domain>/
│ ├── <domain>.controller.ts # Maneja requests/responses HTTP
│ ├── <domain>.service.ts # Lógica de negocio
│ ├── <domain>.repository.ts # Acceso a base de datos
│ ├── <domain>.routes.ts # Definición de rutas
│ ├── <domain>.dto.ts # Data Transfer Objects (validación de entrada)
│ └── <domain>.types.ts # Tipos e interfaces del dominio
├── shared/ # Middleware, errores, utilidades transversales
├── config/ # Configuración (env, db, etc.)
└── app.ts # Bootstrap de Express
Responsabilidad de cada capa
| Archivo | Responsabilidad | No debe |
|---|---|---|
*.routes.ts | Mapear rutas HTTP a métodos del controller, montar middleware | Contener lógica |
*.controller.ts | Leer request, validar con DTO, llamar al service, formar response | Tener lógica de negocio o SQL |
*.dto.ts | Definir y validar la forma de la entrada | Acceder a datos |
*.service.ts | Implementar reglas de negocio del dominio | Conocer detalles de HTTP o SQL crudo |
*.repository.ts | Acceso a la base de datos (queries) | Contener reglas de negocio |
*.types.ts | Tipos e interfaces del dominio | — |
Regla: el flujo de una petición siempre va
routes → controller → service → repository. El controller nunca llama directo al repository, y el service nunca arma respuestas HTTP. Esto mantiene cada capa testeable de forma aislada.
Flujo de una petición autenticada
Persistencia y datos
- PostgreSQL primario: todas las escrituras y la mayoría de lecturas transaccionales.
- Réplica de lectura (read-only): descarga lecturas pesadas (dashboards, reportes) y sirve de base para backups.
- Backups automatizados: programados desde la réplica para no impactar el primario.
- AWS S3: contenido binario de cursos; PostgreSQL guarda únicamente metadatos y referencias.
El cifrado en tránsito y en reposo, y la gestión de secretos, se detallan en Cifrado y Almacenamiento Sensible.
Seguridad e integraciones
- Autenticación: JWT validado en el API Gateway; logins federados con Google y Apple (ver Login con Google y Login con Apple).
- Sesiones: manejo multidispositivo y refresh tokens en Control de Sesiones.
- Pagos: Stripe asume el cumplimiento PCI-DSS; el módulo Subscription crea/verifica suscripciones y consume webhooks (ver Stripe — Suscripciones).
- Privacidad: estrategia GDPR + LFPDPPP en Compliance y Privacidad.
Convenciones aplicadas
- Nomenclatura de archivos:
camelCase(userService.ts); clases/tipos enPascalCase, interfaces con prefijoI. Ver Convenciones de Código. - Código en inglés, comentarios en español.
- TypeScript
strict, prohibidoanysalvo excepción documentada. - CI bloqueante: lint + tests + security audit en verde para mergear.