Eine frühe Version von HiBill konnte auf vier verschiedene Arten festhalten, dass Geld den Besitzer wechselt: als Ausgabe, als Darlehen, als Rückzahlung und als Ausgleich. Jede hatte ihre eigene Tabelle, ihre eigene Ansicht, ihre eigenen Regeln.
Heute sind es zwei. Hier steht, warum, und was wir beim Auseinandernehmen gelernt haben.
Drei Objekte, die heimlich eins waren
Schau dir an, was drei der vier tatsächlich gespeichert haben.
Ein Darlehen war: Geld ging von A an B, und jetzt schuldet B A etwas.
Eine Rückzahlung war: Geld ging von B an A und tilgte einen Teil dessen, was B schuldete.
Ein Ausgleich war: Geld ging von B an A und tilgte einen Teil dessen, was B schuldete – und wir haben ihn als endgültig markiert.
Drei Namen. Ein Ereignis. Geld ging von genau einer Person an genau eine Person. Die Unterschiede lagen alle in der Erzählung – was die Person damit meinte –, nicht in der Struktur. Und wir hatten diese Erzählung im Schema festgeschrieben, wo sie zu drei Codepfaden erstarrte, die für immer synchron gehalten werden mussten.
Verraten hat es das Statusfeld des Ausgleichs. Ausgleiche hatten die Zustände pending, paid und cancelled. Die Oberfläche setzte jedes einzelne Mal paid. Wir hatten einen Zustandsautomaten gebaut und ihn dann nie benutzt, weil man im echten Leben einen Ausgleich erst dann einträgt, wenn das Geld schon geflossen ist. Toter Zustand in einem Schema ist ein starkes Zeichen dafür, dass das Objekt nicht existieren sollte.
Was wirklich anders ist
Nur eine Unterscheidung hat den Kontakt mit der echten Nutzung überlebt: wie viele Personen an jedem Ende stehen.
Eine Ausgabe ist eins-zu-viele. Jemand (oder mehrere) hat bezahlt, und eine Gruppe von Leuten teilt sich die Kosten. Der interessante Teil ist die Aufteilung.

Eine Überweisung ist eins-zu-eins. Geld hat eine Person verlassen und ist vollständig bei einer anderen angekommen. Es gibt keine Aufteilung zu berechnen – die empfangende Person bekommt den ganzen Betrag.
Das ist ein echter struktureller Unterschied, und es ist der einzige. Also sind das die zwei Arten.
Alles, was das alte Modell mit vier Objekten ausdrücken konnte, drückt dieses aus:
Was du festhalten willst | Wie |
|---|---|
Abendessen, auf fünf aufgeteilt | Ausgabe |
Einem Freund €50 leihen | Überweisung |
Die €50 zurückzahlen | Überweisung, in die andere Richtung |
Einen Saldo auf null bringen | Überweisung |
Ausgleichen ist keine besondere Zeremonie mehr. Es hat keine eigene Ansicht und keinen eigenen Status. Es ist eine Überweisung, die einen Saldo eben auf null bringt, und sie steht im Verlauf neben allem anderen, weil sie genau das ist. Eine einzutragen geht wie alles andere: Tipp hab anna 15 gegeben, und die Vorschau liest es als Überweisung – du hast Anna Lopes €15.00 gegeben – ohne Aufteilung, die du prüfen müsstest.

Die Invarianten sind an einen Ort gezogen
Durch das Zusammenlegen der Arten ließen sich die Regeln einmal formulieren und einmal durchsetzen. Es gibt eine einzige Datei, die die Frage „Ist diese Transaktion gültig?“ beantwortet, und sie sagt ungefähr:
Für eine Ausgabe: Die Beträge der Zahlenden müssen, wenn es Zahlende gibt, die Gesamtsumme ergeben – eine Ausgabe kann auch leer anfangen, mit Leuten, die später ergänzt werden oder ihren Anteil über einen geteilten Link wählen. Die Beteiligten schulden Anteile daran.
Für eine Überweisung: genau eine zahlende Person, genau eine empfangende, und der Anteil der empfangenden Person ist der volle Betrag.
Das ist das komplette Regelwerk, das von der Art abhängt. Als wir vier Objekte hatten, waren gleichwertige Regeln über vier Services verstreut und drifteten auseinander – der Pfad für mehrere Zahlende hatte Prüfungen, die der Ausgleichspfad nicht hatte. Jetzt gibt es eine Tür, und alles geht durch sie hindurch.
Die eine Ausnahme
Es gibt eine dritte Art in der Datenbank, die du nie selbst anlegst: Vereinfachung.
Wenn das System einen Schuldenkreis entdeckt – du schuldest Marco etwas, Marco schuldet Aya etwas, Aya schuldet dir etwas –, kann es diesen Kreis verkleinern, ohne etwas daran zu ändern, wie viel jemand unterm Strich aus eigener Tasche zahlt. Diese Anpassungen müssen als echte Einträge festgehalten werden, sonst würden sich die Salden ohne Erklärung bewegen.
Also sind es Transaktionen, beschriftet mit Vereinfachung, die im Verlauf stehen, wo du sie sehen kannst. Die Regel, an die wir uns gehalten haben: Kein Saldo ändert sich ohne sichtbaren Grund. Eine Zahl, die still wegdriftet, ist schlimmer als eine Zahl, die etwas zu groß ist.
Hat es sich gelohnt?
Der Umbau hat mehr Code gelöscht als hinzugefügt, und das ist meist ein gutes Zeichen. Der eigentliche Gewinn lag aber darin, was danach leichter wurde.
Offene Aufteilungen, das Zuordnen von Belegpositionen, Salden in mehreren Währungen, das Hinzufügen von Leuten, die sich nicht registriert haben – jedes davon hätte vorher vier Implementierungen gebraucht, eine pro Objekt, und deshalb war jedes still aufgeschoben worden. Mit zwei Arten und gemeinsamen Invarianten waren es nur noch kleine Aufgaben.
Das ist das Argument dafür, ein Datenmodell zu vereinfachen, und es geht dabei nicht wirklich um Eleganz. Jedes zusätzliche Objekt in einem Schema ist ein Multiplikator auf die Kosten jeder Funktion, die du je hinzufügen wirst. Wir haben bei allem das Vierfache bezahlt, um Unterscheidungen zu bewahren, die nur in unseren eigenen Köpfen existierten.
Die zwei Arten sind heute in HiBill im Einsatz. Wenn du genauer wissen willst, wie Salden darauf aufbauen, ist Salden verstehen das passende Gegenstück.
Weiterlesen
So funktioniert HiBill: das Kassenbuch für Geld unter Freunden – das ganze Modell in einem Text: Personen, Salden und die zwei Arten von Transaktionen.
Schuldenvereinfachung erklärt: So heben sich Schulden im Kreis auf – woher die dritte, vom System angelegte Art kommt.
Wir rechnen deine Währungen nicht um – mit Absicht – eine weitere Entscheidung im Datenmodell, die Salden ehrlich hält.