ProyectosCaso de estudio

MyDebts

Proyecto personal

Una demo completa de una aplicación para gestionar deudas entre personas, con deudas marcadas automáticamente como vencidas y enlaces para compartir deudas sin necesidad de registro.

CLIENTE
Proyecto personal
SECTOR
Finanzas personales
ROL
Todo
CRONOLOGÍA
En desarollo
SITIO EN VIVO
Ver sitio en vivo
Página de inicio de MyDebts
01 / EL PROBLEMA

Recordar quién debe qué no debería depender de la memoria

Las deudas entre amigos y familiares suelen llevarse de forma informal: una nota mental, un hilo de mensajes, una hoja en una libreta... hasta que alguien lo olvida y sacar el tema se vuelve incómodo. MyDebts es una aplicación simple y clara para registrar deudas individuales, cada una con su propia fecha de vencimiento, para que nada dependa de la memoria.

LAS LIMITACIONES QUE LO DEFINIERON TODO
  • Desarrollado en solitario, en tardes y fines de semana, durante mi tiempo libre. La arquitectura tenía que ser sencilla para facilitar el desarrolo tras largos periodos sin actividad.
  • Alojamiento en niveles gratuitos durante todo el proyecto
  • Una demo completa: autenticación real, recuperación de contraseña y protección contra bots.
  • Un despliegue real en vivo, accesible para cualquiera con quien lo compartiera, no solo una build local.
02 / QUÉ CONSTRUÍ

Un frontend en Angular 20 zoneless respaldado por una pequeña API en Express.

Angular 20 con componentes standalone en todo el proyecto, usando detección de cambios zoneless y signals para el estado local en lugar de una librería de estado. El backend es Express 5 con MongoDB a través de Mongoose, validado con Joi, además de un flujo completo de autenticación JWT: registro, inicio de sesión, recuperación de contraseña y protegido con Cloudflare Turnstile.

03 / DECISIONES

Cinco decisiones, y lo que costó cada una.

01

Angular zoneless, no la detección de cambios por defecto

Angular 20 lanzó la detección de cambios zoneless como una opción real, no solo un experimento, y quise construir directamente sobre ella en lugar del zone.js por defecto que la mayoría de tutoriales todavía enseña. Las llamadas a signal() locales sustituyen a NgRx: la aplicación no necesitaba una librería de gestión de estado, así que no añadí una. YAGNI aplicado a una decisión de arquitectura, no solo a una función suelta.

COMPROMISOMenos ejemplos en los que apoyarme cuando algo no se comportaba como se documentaba o surgia algun bug. Zoneless es más reciente, así que la mayoría de respuestas en Stack Overflow todavía hablan de zone.js.
02

Un arranque en frío visible, no oculto

El backend se duerme en el nivel gratuito de Render, así que la primera petición tras un periodo de inactividad puede tardar varios segundos. Un interceptor de peticiones inicia un temporizador y, pasados tres segundos, muestra un aviso de 'despertando el servidor' y reintenta automáticamente, en lugar de dejar la interfaz con aspecto de estar bloqueada.

COMPROMISOEl aviso es un recordatorio visible de que esto corre sobre infraestructura de nivel gratuito, no con la disponibilidad de un entorno profesional. Prefiero mostrarlo con honestidad antes que ocultarlo tras un spinner idéntico tanto si la petición se resuelve en 200ms como en 8 segundos.
03

Instantáneas de solo lectura para los enlaces compartidos, no acceso en vivo

Compartir una deuda tenía que funcionar sin crear una cuenta para la otra persona. Un enlace compartido genera un token que apunta a una instantánea de la deuda en ese momento, no a una referencia en vivo, y caduca automáticamente a las 48 horas mediante un índice TTL de MongoDB.

COMPROMISOLa otra persona no puede actualizar ni editar el registro a través del enlace, cualquier corrección tiene que pasar por quien es propietario de la deuda. Para un caso de uso informal entre dos personas, eso es una funcionalidad, no una carencia.
04

Cálculo bajo demanda como red de seguridad, junto al cron job

Un cron job marca las deudas vencidas una vez al día, pero el backend se duerme en el nivel gratuito de Render y ese cron job puede no ejecutarse si el servidor está inactivo en ese momento. Como red de seguridad, getDebts() también recalcula el estado de vencimiento bajo demanda en cada llamada, así que aunque el cron job falle, la lectura nunca devuelve un estado desactualizado.

COMPROMISOTener dos mecanismos calculando lo mismo añade una pequeña redundancia, pero en un backend con disponibilidad intermitente, es una redundancia deliberada, no una duplicación accidental.
05

Migración de Karma a Vitest para compatibilidad con zoneless

Zoneless requiere patrones explícitos que Karma no siempre gestionaba bien en este proyecto: detección de cambios manual en tests, imports concretos como BrowserTestingModule y platformBrowserTesting. Migrar a Vitest resolvió esos problemas de compatibilidad y de paso redujo el tiempo de ejecución de la suite.

COMPROMISOReescribir la configuración de testing tiene un coste de tiempo que no se ve en la funcionalidad final, pero era necesario para que la suite fuera más fiable.
04 / RESULTADO

Sin cliente, ni ingresos. Sólo un proyecto personal

Zoneless, de forma deliberada

Signals y detección de cambios zoneless usados como modelo de estado principal en lugar de NgRx, no añadidos a posteriori.

Una suite de tests migrada

Karma sustituido por Vitest ya habiendo empezado el proyecto, para resolver problemas reales de compatibilidad con zoneless.

Un sistema completo

Autenticación JWT completa, recuperación de contraseña y protección contra bots, no una demo que se salta las partes menos vistosas.

Vencimiento que no depende de un solo mecanismo

Cron job diario respaldado por cálculo bajo demanda en getDebts(), para que un servidor dormido no deje datos desactualizados.

Este proyecto no tiene cifras de conversión que reportar, porque no tiene cliente. Lo que tiene son dos repositorios públicos disponibles para consultar y un despliegue completo. Siempre dispuesto a aceptar feedback!

SIGUIENTE CASO

Cofers.mx

Rediseño de la página de inicio y creación de una nueva página, construidas a partir de los diseños en Figma del cliente y adaptándolos para móvil, junto a una optimización de rendimiento completa.

Leer más