(04) Systeme / Fallstudie
yourFinance
Die Salden garantiert die Datenbank.
yourFinance ist eine mehrbenutzerfähige App für persönliche Finanzen: eine Rust-API, PostgreSQL und ein einziger Client für iOS, Android, Web und Desktop. Der Code ist privat; diese Seite zeigt, wie sie gebaut ist und warum.
- Beginn
- Juli 2025
- Version
- 1.2.0, Oktober 2026
- Team
- Zwei Entwickler
- Code
- Privat
Rust · Axum · SQLx · PostgreSQL 18 · Expo · React Native · Tauri · Docker
Das Problem
Persönliche Finanzdaten nützen nur, wenn man den Salden trauen kann. Mehrere Personen nutzen die App auf mehreren Geräten, und ein Fehler in einem Client oder in einem Handler darf kein Konto mit einem falschen Betrag zurücklassen können.
Sie muss außerdem mehrere Währungen verarbeiten, die gesamte Historie einer anderen App (Money Manager) übernehmen und Statistiken zeigen, die nie veraltet sind.
Rahmenbedingungen
- Die API schreibt nie einen Saldo: Jede Änderung ist eine Buchung, auch die Korrektur.
- Jede persönliche Zeile gehört einem Benutzer, und PostgreSQL wiederholt die Regel mit Row-Level Security auf jeder Verbindung des Pools.
- Ein Code für iOS, Android und Web; die Desktop-App ist der Web-Export in einer Tauri-Hülle.
- Eine Änderung am Schema oder an der API muss den Build brechen, nicht die laufende App: SQL wird beim Kompilieren geprüft, und die Typen des Clients werden aus dem OpenAPI-Dokument erzeugt.
Architektur
Drei Ebenen: Der Client stellt nur dar, die API authentifiziert, validiert und aggregiert, die Datenbank ist die Quelle der Wahrheit. Die API ist ein Cargo-Workspace aus geschichteten Crates: Jedes hängt nur von den darunterliegenden ab, und die CI prüft, dass die Domäne frei von I/O bleibt.
yourf-api
HTTP: schlanke Routen, Authentifizierung, Fehlerabbildung, OpenAPI.
yourf-worker
Hintergrundjobs: Push-Benachrichtigungen, Zusammenfassung der Warnungen, wiederkehrende Buchungen, Abgleich der Salden.
yourf-runtime
Was beide Binaries teilen: Konfiguration, Telemetrie, Lebenszyklus.
yourf-db
SQLx-Repositories, Geltungsbereich der Row-Level Security, Schreibvorgänge in einer Transaktion.
yourf-import
Leser für Dateien anderer Apps in ein neutrales Format, ohne Datenbank.
yourf-domain
Modelle, Verträge und Geschäftsregeln. Kein I/O.
Zwei Binaries, ein Image: API und Worker werden getrennt ausgerollt und skaliert.
Eine Anfrage, von Anfang bis Ende
Das Anlegen einer Buchung:
- 01Client
Das Formular prüft den Entwurf und sendet ihn mit dem Token.
- 02API
Sie authentifiziert das Token, lädt den Benutzer, prüft den Inhalt und fügt die Buchung mit ihren Aufteilungen nach Konto und Kategorie in einer Transaktion ein.
- 03Datenbank
Verzögerte Trigger prüfen, dass die Aufteilungen aufgehen und dass Konten, Kategorien und Tags dem Benutzer gehören, und aktualisieren dann die Salden. Ein Verstoß rollt alles zurück und wird zu einem 400 mit einem stabilen Regelcode.
- 04Client
Bei Erfolg werden Listen, Summen und Dashboard neu geladen.
Entscheidungen und Abwägungen
- 01
Die Integrität liegt in der Datenbank
Salden, Eigentum an den Daten und Kategorieregeln werden von Triggern durchgesetzt. Das heißt Logik in SQL statt in Rust; dafür kann kein Client und kein Handler die Daten beschädigen.
- 02
Prüfbare Salden statt vollständiger doppelter Buchführung
Jede Wirkung auf einen Saldo ist eine vorzeichenbehaftete Buchungszeile mit einer einzigen Definition, die sowohl der Trigger nutzt, der die Salden schreibt, als auch der Job, der sie abgleicht. Eine vollständige doppelte Buchführung wäre den Umbau nur wert, wenn eine Funktion eine Bilanz bräuchte.
- 03
Ein Übertrag ist eine Buchung mit zwei Beträgen
Was das Quellkonto verlassen hat und was am Ziel angekommen ist. Die Differenz deckt einen Währungstausch ebenso ab wie eine Gebühr, die deshalb in den Statistiken nicht als Ausgabe erscheint.
- 04
Keine Job-Warteschlange
Der Worker holt die fälligen Zeilen mit FOR UPDATE SKIP LOCKED, und die Warteschlange der Push-Benachrichtigungen wird über LISTEN/NOTIFY geweckt. Jeder Durchlauf ist idempotent: Zweimal oder auf mehreren Replikaten ausgeführt, liefert er dasselbe Ergebnis.
- 05
Statistiken bei jeder Anfrage berechnet
Dashboard und Statistiken aggregieren die Buchungen jedes Mal in SQL: keine vorberechneten Aggregate und kein Cache, also sind die Zahlen nie veraltet. Der Preis: Die Abfragen müssen von sich aus schnell sein.
Messwerte
Das Profiling der Analyseabfragen zeigte zwei, deren Kosten mit der gesamten Historie wuchsen statt mit dem gewählten Zeitraum. Mit dem generischen Plan eines Prepared Statements wählte der Planer einen Nested Loop, der einen Join für jede Buchung erneut ausführte. An den Indizes lag es nicht: Die Korrektur bestand darin, die View so umzuschreiben, dass jeder Zweig bei der Buchung beginnt.
| Abfrage | Vorher | Nachher |
|---|---|---|
| Tägliche Flüsse (Verlauf der Statistiken)daily_flows | 64.5 ms | 1.9 ms |
| Notwendige Ausgaben (Zusammenfassung des Dashboards)essential_expenses | 48.0 ms | 1.5 ms |
Die App



Wie es weitergeht
- Ein Ablaufdatum für Konten, mit einem Hinweis, bevor es erreicht ist.
- Abschreibung: Kosten, die in den Statistiken über mehrere Monate verteilt werden, während der Saldo kassenbasiert bleibt.
- Ein Assistent, der Fragen zu Statistiken und Konten mit Nur-Lese-Werkzeugen beantwortet: Das Modell wählt das Werkzeug, jede Zahl berechnet das Backend.
Code und Demo
Das Repository ist privat. Auf Anfrage zeige ich den Code und die laufende App.