Ir al contenido

(04) Sistemas / Caso de estudio

yourFinance

Los saldos los garantiza la base de datos.

yourFinance es una app de finanzas personales multiusuario: una API en Rust, PostgreSQL y un solo cliente para iOS, Android, web y escritorio. El código es privado; esta página explica cómo está hecha y por qué.

Inicio
Julio de 2025
Versión
1.3.0, octubre de 2026
Equipo
Dos desarrolladores
Código
Privado

Rust · Axum · SQLx · PostgreSQL 18 · Expo · React Native · Tauri · Docker

El problema

Los datos de finanzas personales solo sirven si uno se puede fiar de los saldos. Varias personas usan la app desde varios dispositivos, y un error en un cliente o en un handler no puede dejar una cuenta con una cifra equivocada.

Además tiene que manejar más de una divisa, importar el historial completo de otra app (Money Manager) y mostrar estadísticas que nunca estén desfasadas.

Restricciones

  • La API nunca escribe un saldo: cada cambio es un movimiento, también un ajuste de saldo.
  • Cada fila personal pertenece a un usuario, y PostgreSQL repite la regla con seguridad a nivel de fila en cada conexión del pool.
  • Un solo código para iOS, Android y web; la app de escritorio es la versión web dentro de Tauri.
  • Un cambio en el esquema o en la API tiene que romper la compilación, no la app en uso: el SQL se comprueba al compilar y los tipos del cliente se generan a partir del documento OpenAPI.

Arquitectura

Tres niveles: el cliente solo muestra, la API autentica, valida y agrega, y la base de datos es la fuente de verdad. La API es un workspace de Cargo con crates por capas: cada uno depende solo de los que están por debajo, y la CI comprueba que el dominio no haga E/S.

  1. yourf-api

    HTTP: rutas finas, autenticación, traducción de errores, OpenAPI.

    yourf-worker

    Tareas en segundo plano: notificaciones push, resumen de alertas, movimientos recurrentes, conciliación de saldos.

  2. yourf-runtime

    Lo que comparten los dos binarios: configuración, telemetría, ciclo de vida.

  3. yourf-db

    Repositorios SQLx, ámbito de la seguridad a nivel de fila, escrituras en una sola transacción.

    yourf-import

    Lectores de archivos de otras apps a un formato neutro, sin base de datos.

  4. yourf-domain

    Modelos, contratos y reglas de negocio. Sin E/S.

Dos binarios, una imagen: API y worker se despliegan y escalan por separado.

Una petición, de principio a fin

Crear un movimiento:

  1. 01Cliente

    El formulario valida el borrador y lo envía con el token.

  2. 02API

    Autentica el token, carga el usuario, valida el cuerpo e inserta el movimiento con sus repartos por cuenta y categoría en una sola transacción.

  3. 03Base de datos

    Unos triggers diferidos comprueban que los repartos cuadren y que cuentas, categorías y etiquetas sean del usuario, y después actualizan los saldos. Si se incumple una regla se deshace todo y la respuesta es un 400 con un código de regla estable.

  4. 04Cliente

    Si todo va bien, se recargan las listas, los totales y el panel.

Decisiones y compromisos

  1. 01

    La integridad está en la base de datos

    Saldos, propiedad de los datos y reglas de las categorías se imponen con triggers. Supone escribir lógica en SQL en lugar de Rust; a cambio, ningún cliente ni ningún handler puede corromper los datos.

  2. 02

    Saldos comprobables, no una contabilidad de partida doble completa

    Cada efecto sobre un saldo es un apunte con signo y una única definición, que usan tanto el trigger que escribe los saldos como la tarea que los concilia. Un libro mayor completo solo compensaría la reescritura si alguna función necesitara un balance.

  3. 03

    Una transferencia es un movimiento con dos importes

    Lo que salió de la cuenta de origen y lo que llegó a la de destino. La diferencia cubre tanto un cambio de divisa como una comisión, que por eso no aparece como gasto en las estadísticas.

  4. 04

    Sin cola de trabajos

    El worker toma las filas pendientes con FOR UPDATE SKIP LOCKED y el outbox de notificaciones se despierta con LISTEN/NOTIFY. Cada pasada es idempotente: ejecutarla dos veces, o en varias réplicas, da el mismo resultado.

  5. 05

    Estadísticas calculadas en cada petición

    El panel y las estadísticas agregan los movimientos en SQL cada vez: sin agregados precalculados ni caché, así que las cifras nunca están desfasadas. El precio es que las consultas tienen que ser rápidas por sí mismas.

Medido

Al perfilar las consultas de estadísticas vi que dos crecían con todo el historial en lugar de con el periodo elegido. Con el plan genérico de una sentencia preparada, el planificador elegía un nested loop que repetía un join una vez por movimiento. Los índices no eran el problema: la solución fue reescribir la vista para que cada rama empiece por el movimiento.

Perfil demo, un año de datos, planes genéricos, medido con EXPLAIN ANALYZE con el rol de la aplicación.
ConsultaAntesDespués
Flujos diarios (tendencia de las estadísticas)daily_flows64.5 ms1.9 ms
Gastos esenciales (resumen del panel)essential_expenses48.0 ms1.5 ms

La app

Panel de yourFinance en escritorio: patrimonio neto, número FIRE, tasa de ahorro, fondo de emergencia y un gráfico del patrimonio en doce meses.
Panel: seis métricas, patrimonio neto en doce meses, distribución de activos. Datos demo.
Pantalla de estadísticas: ingresos, gastos, saldo neto y tasa de ahorro de los últimos doce meses, con un gráfico de barras mensual.
Estadísticas: ingresos y gastos por mes, con filtros y desglose por categoría, cuenta o etiqueta. Datos demo.
Lista de movimientos en un móvil: totales de ingresos y gastos y una tarjeta por movimiento.
El mismo código en un móvil: la lista de movimientos. Datos demo.

Lo que viene

  • Fecha de vencimiento en las cuentas, con un aviso antes de que llegue.
  • Amortización: un gasto repartido en varios meses en las estadísticas, mientras el saldo sigue reflejando la caja.
  • Un asistente que responde preguntas sobre estadísticas y cuentas con herramientas de solo lectura: el modelo elige la herramienta y el backend calcula cada número.

Código y demo

El repositorio es privado. Si me lo pides, te enseño el código y la app en funcionamiento.

EscríbemeVolver a Sistemas