Este documento explica las diferencias arquitectónicas, de diseño y funcionales entre Finward y el sistema legacy previo.
- Estructura plana:
routes/,controllers/,services/globales - Controladores separados de rutas
- Servicios monolíticos con múltiples responsabilidades
- Lógica de negocio dispersa en rutas y servicios
- Clean Architecture modular: cada dominio tiene su carpeta
modules/{domain}/ domain/ → Reglas de negocio puras useCases/ → Casos de uso (una acción = un archivo) infrastructure/ → Rutas REST, adaptadores - Sin controladores genéricos: las rutas delegan directamente a use cases
- Código autodocumentado:
CreateExpenseUseCase,PayInstallmentUseCase - Separación clara: dominio → casos de uso → infraestructura
- Auth manual con JWT propio o middleware custom
- User con
passworden DB (credenciales en backend) - Validaciones de
userIdmanuales en cada ruta
- Supabase Auth nativo: JWT de Supabase,
auth.uid()en RLS - User sin
password: auth delegada a Supabase - RLS en Postgres:
auth.uid() = user_idfiltra automáticamente - Menos código de validación: Postgres hace el filtrado
- Balance calculado vía
increment/decrementenAccount - Riesgo de desincronización si hay errores parciales
- Sin trazabilidad de movimientos
- Ledger de doble partida: cada transacción genera
LedgerEntrycon suma 0 - Balance =
SUM(amount)de LedgerEntries por cuenta - Trazabilidad completa: cada cambio tiene entrada en ledger
- Consistencia: transacciones atómicas con Prisma
- Lógica de corte/pago en controlador grande (~400 líneas)
- Cálculo de MSI y pagos mezclado con UI/HTTP
- Self-healing manual para corregir desincronizaciones
- domain/billingCycle.ts: cálculo de ciclos puro
- domain/nextPaymentDate.ts: próxima cuota MSI
- Use cases:
CreateInstallmentPurchaseUseCase,PayInstallmentUseCase - Pagos de corte y MSI vía
createTransfer+ Ledger
- Lógica de frecuencia compleja (biweekly_15_30, etc.)
- Cálculo de próximas fechas en servicio compartido
- domain/nextDueDate.ts: cálculo de siguiente vencimiento
- Pay/Skip como endpoints explícitos
- Integración con CreateIncome/CreateExpense (recurringTransactionId)
- Controlador separado, crea transacción de gasto si
sourceAccountId
- Módulo
investments/con rutas limpias - Misma lógica: crear Investment + opcionalmente CreateExpense
- Categoría "Inversiones" creada bajo demanda
- Precios automáticos:
POST /investments/refresh-pricesactualiza crypto (CoinGecko) + stock (Yahoo Finance). Frontend: botón "Actualizar precios", indicador "Actualizado hace X min", auto-refresh si >15 min. Ver COINGECKO.md, YAHOO_FINANCE.md
- Controlador ~1000 líneas con lógica de períodos, MSI, 50/30/20
- Mucha lógica de fechas y timezone
- Warnings y budget analysis integrados
- Versión simplificada: summary + upcoming
- Períodos: semanal, quincenal, mensual, bimestral, semestral, anual
- Sin 50/30/20 complejo: datos crudos para que el frontend analice
- Notificaciones con SmartAlertService
- Profile con multer para avatar (uploads locales)
- Notificaciones: CRUD simple, marcado read/read-all
- Profile: Supabase Storage para avatar (ver docs/STORAGE.md)
- Sin multer: uploads 100% en Supabase Storage
- multer + carpeta
uploads/en servidor
- Supabase Storage: buckets públicos/privados
- Ningún archivo en el servidor Node
- Ver
docs/STORAGE.md
| Aspecto | Legacy | Finward |
|---|---|---|
| Estructura | routes/controllers/services | modules/domain/useCases/infrastructure |
| Auth | Manual, password en DB | Supabase Auth, RLS |
| Contabilidad | increment/decrement | Ledger doble partida |
| Validación | Manual en cada ruta | RLS + AppError centralizado |
| MSI/Cortes | Controlador grande | Domain + Use Cases |
| Uploads | multer local | Supabase Storage |
| Deuda técnica | Alta | Cero (reenfoque completo) |