Offline-first Formular-Konfliktlösung für Kotlin + SQLite
Lerne Offline-First Formular-Konfliktauflösung: klare Merge-Regeln, ein einfacher Kotlin + SQLite Sync-Ablauf und praktische UX-Muster für Bearbeitungskonflikte.

Was passiert eigentlich, wenn zwei Personen offline bearbeiten
Offline-first-Formulare ermöglichen es Menschen, Daten anzusehen und zu ändern, auch wenn das Netzwerk langsam oder nicht vorhanden ist. Statt auf den Server zu warten, schreibt die App Änderungen zuerst in eine lokale SQLite-Datenbank und synchronisiert später.
Das fühlt sich sofort an, hat aber eine einfache Konsequenz: Zwei Geräte können denselben Datensatz ändern, ohne voneinander zu wissen.
Ein typischer Konflikt sieht so aus: Ein Techniker öffnet einen Arbeitsauftrag auf einem Tablet im Keller ohne Empfang. Er setzt den status auf "Done" und fügt eine Notiz hinzu. Gleichzeitig aktualisiert ein Vorgesetzter auf einem anderen Telefon denselben Auftrag, weist ihn neu zu und ändert das Fälligkeitsdatum. Beide drücken auf Speichern. Beide Speichervorgänge schlagen lokal fehl. Niemand hat etwas falsch gemacht.
Wenn die Synchronisation schließlich stattfindet, muss der Server entscheiden, welcher Datensatz „real“ ist. Wenn Sie Konflikte nicht explizit behandeln, endet das meistens so:
- Last write wins: Die spätere Synchronisation überschreibt frühere Änderungen und jemand verliert Daten.
- Harte Fehler: Die Synchronisation lehnt ein Update ab und die App zeigt eine wenig hilfreiche Fehlermeldung.
- Doppelte Datensätze: Das System legt eine zweite Kopie an, um Überschreiben zu vermeiden, und das Reporting wird unübersichtlich.
- Stilles Merge: Das System kombiniert Änderungen, mischt dabei aber Felder auf unerwartete Weise.
Konflikte sind kein Bug. Sie sind die vorhersehbare Folge davon, Menschen ohne Live-Verbindung arbeiten zu lassen — genau das Ziel von Offline-first.
Das Ziel ist zweifach: Daten schützen und die App einfach bedienbar halten. Das bedeutet meist klare Merge-Regeln (oft auf Feldebene) und eine UX, die Nutzer nur dann unterbricht, wenn es wirklich nötig ist. Wenn zwei Bearbeitungen unterschiedliche Felder berühren, können Sie meist still zusammenführen. Wenn zwei Personen dasselbe Feld unterschiedlich ändern, sollte die App das sichtbar machen und helfen, das richtige Ergebnis zu wählen.
Wählen Sie eine Konfliktstrategie, die zu Ihren Daten passt
Konflikte sind zuerst eine Produktentscheidung, nicht nur ein technisches Problem: Was bedeutet „korrekt“, wenn zwei Personen denselben Datensatz vor der Synchronisation geändert haben?
Drei Strategien decken die meisten Offline-Apps ab:
- Last write wins (LWW): Die neueste Änderung wird akzeptiert und überschreibt die ältere.
- Manuelle Überprüfung: Anhalten und einen Menschen entscheiden lassen, was behalten wird.
- Feldbasierter Merge: Änderungen werden pro Feld kombiniert und nur bei Konkurrenz in einem Feld nachgefragt.
LWW ist passend, wenn Tempo wichtiger ist als perfekte Genauigkeit und die Kosten eines Fehlers gering sind. Denken Sie an interne Notizen, unkritische Tags oder einen Entwurfstatus, der später erneut bearbeitet werden kann.
Manuelle Überprüfung ist die sichere Wahl für Felder mit hoher Auswirkung: Rechtstexte, Compliance-Bestätigungen, Gehalts- und Rechnungsbeträge, Bankdaten, Medikationsanweisungen und alles, was Haftung erzeugen kann.
Feldbasierter Merge ist meist die beste Voreinstellung für Formulare, bei denen unterschiedliche Rollen verschiedene Teile pflegen. Ein Support-Mitarbeiter ändert die Adresse, während Sales das Verlängerungsdatum aktualisiert. Ein Merge pro Feld behält beide Änderungen ohne Nutzerbeteiligung. Wenn jedoch beide das Verlängerungsdatum geändert haben, sollte dieses Feld eine Entscheidung auslösen.
Bevor Sie irgendetwas implementieren, schreiben Sie auf, was „korrekt“ für Ihr Business bedeutet. Eine schnelle Checkliste hilft:
- Welche Felder müssen immer den aktuellsten realen Wert zeigen (z. B.
status)? - Welche Felder sind historisch und dürfen nie überschrieben werden (z. B. ein eingereichtes Zeitstempel-Feld)?
- Wer darf welches Feld ändern (Rollen, Eigentum, Genehmigungen)?
- Was ist die Quelle der Wahrheit bei widersprüchlichen Werten (Gerät, Server, Manager-Genehmigung)?
- Was passiert, wenn Sie falsch liegen (kleine Unannehmlichkeit vs. finanzieller oder rechtlicher Schaden)?
Sind diese Regeln klar, hat der Sync-Code eine Aufgabe: sie durchzusetzen.
Merge-Regeln pro Feld definieren, nicht pro Bildschirm
Bei einem Konflikt betrifft er selten das ganze Formular gleichmäßig. Ein Nutzer ändert vielleicht die Telefonnummer, ein anderer fügt eine Notiz hinzu. Behandeln Sie den gesamten Datensatz nicht als Alles-oder-Nichts, sonst zwingen Sie Leute, gute Arbeit zu wiederholen.
Feldbasierter Merge ist vorhersagbarer, weil jedes Feld ein bekanntes Verhalten hat. Die UX bleibt ruhig und schnell.
Ein einfacher Einstieg ist, Felder in „meistens sicher“ und „meistens unsicher“ zu trennen.
Meistens sicher automatisch zu mergen: Notizen und interne Kommentare, Tags, Anhänge (oft Vereinigung) und Zeitstempel wie last contacted (oft das neueste behalten).
Meistens unsicher automatisch zu mergen: status/Zustand, assignee/Zuweisung, Summen/Preise, Genehmigungsflags und Lagerbestände.
Wählen Sie dann eine Prioritätsregel pro Feld. Gängige Optionen sind: Server gewinnt, Client gewinnt, Rollenbasiert (z. B. Manager überschreibt Agent), oder ein deterministischer Tie-Breaker wie die neueste Server-Version.
Die Kernfrage ist, was passiert, wenn beide Seiten dasselbe Feld geändert haben. Für jedes Feld wählen Sie ein Verhalten:
- Auto-merge mit klarer Regel (z. B. Tags sind eine Vereinigung)
- Beide Werte behalten (z. B. Notizen anhängen mit Autor und Zeit)
- Zur Überprüfung markieren (z. B.
statusundassigneeerfordern eine Entscheidung)
Beispiel: Zwei Support-Mitarbeiter bearbeiten offline dasselbe Ticket. Rep A ändert den status von "Open" auf "Pending". Rep B ändert notes und fügt das Tag "refund" hinzu. Beim Sync können notes und tags sicher gemerged werden, aber status sollte nicht stillschweigend zusammengeführt werden. Fragen Sie nur beim status, alles andere ist bereits gemerged.
Um späteren Diskussionen vorzubeugen, dokumentieren Sie jede Regel in einem Satz pro Feld:
- "
notes: Beide behalten, neueste anhängen, Autor und Zeit hinzufügen." - "
tags: Vereinigung, nur entfernen, wenn auf beiden Seiten explizit entfernt." - "
status: Wenn auf beiden Seiten geändert, Benutzerentscheidung erforderlich." - "
assignee: Manager gewinnt, sonst Server gewinnt."
Dieser Ein-Satz-Regel wird zur Quelle der Wahrheit für Kotlin-Code, SQLite-Abfragen und die Konflikt-UI.
Datenmodell-Grundlagen: Versionen und Audit-Felder in SQLite
Wenn Konflikte vorhersehbar wirken sollen, fügen Sie jeder synchronisierten Tabelle eine kleine Menge Metadatenspalten hinzu. Ohne sie können Sie nicht sagen, ob Sie einen frischen Edit, eine alte Kopie oder zwei zu merge-ende Edits sehen.
Ein praktisches Minimum für jeden server-synchronisierten Datensatz:
id(stabile Primärschlüssel): niemals wiederverwendenversion(Integer): erhöht sich bei jedem erfolgreichen Schreibvorgang auf dem Serverupdated_at(Timestamp): wann der Datensatz zuletzt geändert wurdeupdated_by(Text oder Benutzer-ID): wer die letzte Änderung gemacht hat
Auf dem Gerät fügen Sie lokale Felder hinzu, um Änderungen zu verfolgen, die noch nicht vom Server bestätigt wurden:
dirty(0/1): lokale Änderungen vorhandenpending_sync(0/1): zum Hochladen bereit, aber noch nicht bestätigtlast_synced_at(Timestamp): letzte Zeit, zu der diese Zeile mit dem Server übereinstimmtesync_error(Text, optional): letzter Fehlergrund zur Anzeige in der UI
Optimistische Parallelitätskontrolle ist die einfachste Regel, die stille Überschreibungen verhindert: Jedes Update beinhaltet die Version, die Sie zu bearbeiten glauben (expected_version). Ist der Server noch auf dieser Version, wird das Update akzeptiert und der Server liefert die neue Version zurück. Falls nicht, entsteht ein Konflikt.
Beispiel: Nutzer A und B haben beide version = 7 heruntergeladen. A synchronisiert zuerst; der Server erhöht auf 8. Wenn B nun mit expected_version = 7 synchronisiert, lehnt der Server mit einem Konflikt ab, sodass Bs App mergen kann, statt zu überschreiben.
Für eine gute Konflikt-Anzeige speichern Sie den gemeinsamen Ausgangspunkt: von welchem Stand hat der Nutzer ursprünglich editiert. Zwei gängige Ansätze:
- Eine Momentaufnahme des zuletzt synchronisierten Datensatzes speichern (eine JSON-Spalte oder eine parallele Tabelle).
- Ein Änderungsprotokoll speichern (Zeile-pro-Edit oder Feld-pro-Edit).
Snapshots sind einfacher und oft ausreichend für Formulare. Change-Logs sind aufwendiger, erklären aber genau, was Feld für Feld geändert wurde.
In beiden Fällen sollte die UI drei Werte pro Feld anzeigen können: die Bearbeitung des Nutzers, den aktuellen Wert auf dem Server und den gemeinsamen Ausgangspunkt.
Datensatz-Snapshots vs. Änderungsprotokolle: Wählen Sie einen Ansatz
Beim Synchronisieren von Offline-First-Formularen können Sie den kompletten Datensatz (Snapshot) hochladen oder eine Liste von Operationen (Change-Log). Beide funktionieren mit Kotlin und SQLite, verhalten sich aber unterschiedlich, wenn zwei Personen denselben Datensatz bearbeiten.
Option A: Ganz-Record-Snapshots
Bei Snapshots schreibt jeder Speichervorgang den aktuellen Vollzustand (alle Felder). Beim Sync senden Sie den Datensatz plus Versionsnummer. Erkennt der Server eine alte Version, haben Sie einen Konflikt.
Das ist einfach zu bauen und schnell zu lesen, erzeugt aber oft größere Konflikte als nötig. Wenn Nutzer A die Telefonnummer ändert und Nutzer B die Adresse, kann ein Snapshot das als großen Zusammenstoß behandeln, obwohl die Änderungen sich nicht überlappen.
Option B: Change-Logs (Operationen)
Bei Change-Logs speichern Sie, was geändert wurde, nicht den gesamten Datensatz. Jede lokale Änderung wird zu einer Operation, die auf dem neuesten Serverzustand wieder abgespielt werden kann.
Operationen, die sich oft leichter mergen lassen:
- Feldwert setzen (z. B.
emailauf einen neuen Wert setzen) - Notiz anhängen (fügt ein neues Notiz-Element hinzu)
- Tag hinzufügen (fügt ein Tag zu einer Menge hinzu)
- Tag entfernen (entfernt ein Tag aus der Menge)
- Checkbox als erledigt markieren (setzt
isDonetrue mit Zeitstempel)
Operation-Logs reduzieren Konflikte, weil viele Aktionen nicht kollidieren. Notizen anhängen kollidiert selten mit einer anderen Notiz. Tag-Adds/Removes verhalten sich wie Mengenoperationen. Bei Single-Value-Feldern benötigen Sie weiterhin Regeln, wenn zwei verschiedene Edits konkurrieren.
Der Kompromiss ist Komplexität: stabile Operation-IDs, Reihenfolge (lokale Sequenz und Server-Zeit) und Regeln für nicht-kommutative Operationen.
Aufräumen: Compacting nach erfolgreichem Sync
Operation-Logs wachsen, planen Sie also, wie Sie sie schrumpfen.
Ein gängiger Ansatz ist kompakt pro Datensatz: Sobald alle Operationen bis zu einer bekannten Server-Version bestätigt sind, fassen Sie sie in einen neuen Snapshot zusammen und löschen die älteren Operationen. Behalten Sie nur einen kurzen Tail, wenn Sie Undo, Audit oder einfacheres Debugging brauchen.
Schritt-für-Schritt-Sync-Ablauf für Kotlin + SQLite
Eine gute Sync-Strategie besteht größtenteils darin, strikt zu sein, was Sie senden und was Sie akzeptieren. Das Ziel ist einfach: niemals neuere Daten versehentlich überschreiben und Konflikte deutlich machen, wenn kein sicherer Merge möglich ist.
Ein praktikabler Ablauf:
-
Jede Änderung zuerst in SQLite schreiben. Speichern Sie Änderungen in einer lokalen Transaktion und markieren Sie den Datensatz mit
pending_sync = 1. Speichern Sielocal_updated_atund die zuletzt bekannteserver_version. -
Senden Sie einen Patch, nicht den ganzen Datensatz. Wenn Verbindung besteht, senden Sie die
idplus nur die geänderten Felder zusammen mitexpected_version. -
Lassen Sie den Server widersprüchliche Versionen ablehnen. Wenn die Server-Version nicht mit
expected_versionübereinstimmt, liefert er eine Konflikt-Payload (Server-Datensatz, vorgeschlagene Änderungen und welche Felder sich unterscheiden). Stimmen die Versionen überein, wendet er den Patch an, erhöht die Version und gibt den aktualisierten Datensatz zurück. -
Zuerst Auto-Merge anwenden, dann den Nutzer fragen. Führen Sie feldbasierte Merge-Regeln aus. Behandeln Sie sichere Felder wie Notizen anders als sensible Felder wie
status, Preis oderassignee. -
Endergebnis committen und Pending-Flags löschen. Ob auto-merged oder manuell gelöst: Schreiben Sie den finalen Datensatz zurück nach SQLite, aktualisieren Sie
server_version, setzen Siepending_sync = 0und speichern Sie genug Audit-Daten, um später erklären zu können, was passiert ist.
Beispiel: Zwei Vertriebsmitarbeiter bearbeiten offline dieselbe Bestellung. Rep A ändert das Lieferdatum. Rep B ändert die Kunden-Telefonnummer. Mit Patches kann der Server beide Änderungen sauber akzeptieren. Wenn beide das Lieferdatum geändert haben, zeigen Sie eine klare Entscheidung an, anstatt eine komplette Neueingabe zu erzwingen.
Halten Sie das UI-Versprechen konsistent: "Saved" sollte lokal gespeichert bedeuten. "Synced" ist ein separater, expliziter Zustand.
UX-Muster zur Lösung von Konflikten in Formularen
Konflikte sollten die Ausnahme bleiben, nicht der Normalfall. Beginnen Sie damit, sichere Felder automatisch zu mergen und fragen Sie den Nutzer nur, wenn eine Entscheidung wirklich nötig ist.
Konflikte selten machen mit sicheren Voreinstellungen
Wenn zwei Personen unterschiedliche Felder bearbeiten, mergen Sie ohne Modal. Behalten Sie beide Änderungen und zeigen Sie eine kleine "Updated after sync"-Mitteilung.
Prompts sind nur für echte Kollisionen reserviert: dasselbe Feld auf beiden Seiten geändert, oder eine Änderung hängt von einem anderen Feld ab (z. B. status plus Statusgrund).
Wenn Sie fragen müssen, machen Sie es schnell
Ein Konfliktbildschirm sollte zwei Dinge beantworten: Was hat sich geändert, und was wird gespeichert. Vergleichen Sie Werte nebeneinander: "Ihre Änderung", "Ihre Kollegen-Änderung" und "Gespeichertes Ergebnis". Wenn nur zwei Felder konfligieren, zeigen Sie nicht das ganze Formular. Springen Sie direkt zu diesen Feldern und machen Sie den Rest schreibgeschützt.
Begrenzen Sie die Aktionen auf das, was Nutzer wirklich brauchen:
- Keep mine
- Keep theirs
- Edit final
- Review field-by-field (nur wenn nötig)
Partielle Merges sind die Stelle, an der UX kompliziert wird. Heben Sie nur die konfliktbehafteten Felder hervor und kennzeichnen Sie die Quelle klar ("Yours" und "Theirs"). Wählen Sie vorausselbst die sicherste Option, damit der Nutzer bestätigen und weitermachen kann.
Setzen Sie Erwartungen, damit sich Nutzer nicht gefangen fühlen. Sagen Sie, was passiert, wenn sie weggehen: z. B. "Wir behalten Ihre Version lokal und versuchen später erneut zu synchronisieren" oder "Dieser Datensatz bleibt in Needs review, bis Sie wählen." Machen Sie diesen Zustand in der Listenansicht sichtbar, damit Konflikte nicht verloren gehen.
Wenn Sie diesen Ablauf in AppMaster implementieren, gilt derselbe UX-Ansatz: zuerst sichere Felder auto-merge, dann eine fokussierte Review, wenn Felder wirklich kollidieren.
Knifflige Fälle: Löschen, Duplikate und "fehlende" Datensätze
Die meisten Synchronisationsprobleme, die zufällig wirken, entstehen aus drei Situationen: Jemand löscht, während ein anderer bearbeitet; zwei Geräte erstellen offline dasselbe Ding; oder ein Datensatz verschwindet und taucht später wieder auf. Diese Fälle brauchen explizite Regeln, weil LWW oft überrascht.
Löschen vs. Bearbeiten: Wer gewinnt?
Entscheiden Sie, ob Löschen stärker ist als Bearbeiten. In vielen Business-Apps gewinnt Löschen, weil Nutzer erwarten, dass entfernte Datensätze entfernt bleiben.
Ein praktisches Regelwerk:
- Wird ein Datensatz auf irgendeinem Gerät gelöscht, behandeln Sie ihn überall als gelöscht, auch wenn es spätere Edits gibt.
- Wenn Löschen reversibel sein muss, wandeln Sie "Löschen" in einen Archiv-Status statt eines harten Löschens um.
- Kommt ein Edit für einen gelöschten Datensatz an, behalten Sie die Änderung in der Historie für Audit-Zwecke, stellen den Datensatz aber nicht wieder her.
Offline-Erstellungs-Kollisionen und doppelte Entwürfe
Offline-First-Formulare erstellen oft temporäre IDs (z. B. UUID) bevor der Server eine finale ID vergibt. Duplikate entstehen, wenn Nutzer zwei Entwürfe für dasselbe reale Objekt erstellen (denselben Beleg, dasselbe Ticket, denselben Artikel).
Wenn Sie einen stabilen Natural Key haben (Belegnummer, Barcode, E-Mail plus Datum), nutzen Sie ihn zur Kollisions-Erkennung. Falls nicht, akzeptieren Sie Duplikate und bieten später eine einfache Merge-Option an.
Implementierungstip: Speichern Sie sowohl local_id als auch server_id in SQLite. Wenn der Server antwortet, schreiben Sie eine Zuordnung und behalten Sie diese mindestens so lange, bis Sie sicher sind, dass keine wartenden Änderungen noch auf die lokale ID referenzieren.
"Wiederauferstehung" nach Sync verhindern
Wiederauferstehung passiert, wenn Gerät A einen Datensatz löscht, Gerät B offline ist und später eine alte Kopie als Upsert hochlädt und damit den Datensatz wiederherstellt.
Die Lösung ist ein Tombstone. Markieren Sie die Zeile statt sie sofort zu entfernen als gelöscht mit deleted_at (häufig auch deleted_by und delete_version). Während der Synchronisation behandeln Sie Tombstones als echte Änderungen, die ältere nicht-gelöschte Zustände überschreiben können.
Entscheiden Sie, wie lange Sie Tombstones aufbewahren. Wenn Nutzer wochenlang offline sein können, bewahren Sie sie länger auf. Löschen Sie sie erst, wenn Sie sicher sind, dass aktive Geräte die Löschversion synchronisiert haben.
Wenn Sie Undo unterstützen, behandeln Sie Undo als weitere Änderung: deleted_at zurücksetzen und die Version erhöhen.
Häufige Fehler, die Datenverlust oder Frust verursachen
Viele Sync-Fehler resultieren aus kleinen Annahmen, die leise gute Daten überschreiben.
Fehler 1: Gerätezeit zur Reihenfolge von Änderungen vertrauen
Telefone können falsche Uhren haben, Zeitzonen wechseln und Nutzer die Zeit manuell ändern. Ordnen Sie Änderungen nicht nach Gerätezeit, sonst wenden Sie Edits irgendwann in falscher Reihenfolge an.
Bevorzugen Sie serverausgegebene Versionen (monoton wachsendes serverVersion) und behandeln Sie Client-Zeiten als rein anzeigbar. Falls Zeit unvermeidbar ist, bauen Sie Sicherungen und reconciliations auf dem Server ein.
Fehler 2: Aus Versehen LWW bei sensiblen Feldern
LWW wirkt simpel, bis es Felder trifft, die nicht einfach vom später synchronisierenden Gerät "gewinnen" dürfen. status, Summen, Genehmigungen und Zuweisungen brauchen meist explizite Regeln.
Eine Sicherheits-Checkliste für Risikofelder:
- Behandeln Sie Status-Übergänge als Zustandsmaschine, nicht als Freitext.
- Rekalkulieren Sie Summen aus Positionen. Mergen Sie keine Rohsummen.
- Bei Zählern mergen Sie durch Anwenden von Deltas, nicht durch Auswählen eines Gewinners.
- Bei Eigentum/Zuweisung verlangen Sie bei Konflikten eine explizite Bestätigung.
Fehler 3: Neuere Serverwerte mit veralteten gecachten Daten überschreiben
Das passiert, wenn der Client einen alten Snapshot editiert und dann den vollen Datensatz hochlädt. Der Server akzeptiert es und neuere serverseitige Änderungen verschwinden.
Ändern Sie die Form Ihrer Updates: Senden Sie nur geänderte Felder (oder ein Change-Log) plus die Basisversion, die Sie editiert haben. Ist diese Basisversion veraltet, lehnt der Server ab oder erzwingt ein Merge.
Fehler 4: Keine "wer hat was geändert"-Historie
Bei Konflikten wollen Nutzer eine Antwort: Was habe ich geändert, und was hat die andere Person geändert? Ohne Identität des Bearbeiters und ein Feld-für-Feld-Audit wird die Konfliktanzeige zur Ratespiel.
Speichern Sie updatedBy, Server-Zeitstempel falls verfügbar, und mindestens eine leichte per-Feld-Auditspur.
Fehler 5: Konflikt-UI, die ganzen Datensätze vergleichen lässt
Nutzer durch ganze Datensätze vergleichen zu lassen ist ermüdend. Die meisten Konflikte betreffen nur ein bis drei Felder. Zeigen Sie nur die konfliktbehafteten Felder, wählen Sie vorausselbst die sichere Option und lassen Sie den Nutzer den Rest automatisch akzeptieren.
Wenn Sie Formulare in einem No-Code-Tool wie AppMaster bauen, streben Sie dasselbe Ergebnis an: Konflikte auf Feldebene lösen, damit Nutzer eine klare Entscheidung treffen, statt durch das ganze Formular zu scrollen.
Kurze Checkliste und nächste Schritte
Wenn Offline-Edits sicher wirken sollen, behandeln Sie Konflikte als normalen Zustand, nicht als Fehler. Beste Ergebnisse entstehen durch klare Regeln, wiederholbare Tests und eine UX, die in einfacher Sprache erklärt, was passiert ist.
Bevor Sie weitere Features hinzufügen, stellen Sie sicher, dass diese Grundlagen sitzen:
- Für jeden Datensatztyp: eine Merge-Regel pro Feld (LWW, max/min, anhängen, Vereinigung oder immer nachfragen).
- Speichern Sie eine serverkontrollierte Version plus ein
updated_at, und validieren Sie beides beim Sync. - Führen Sie einen Zwei-Geräte-Test durch, bei dem beide denselben Datensatz offline bearbeiten und dann in beiden Reihenfolgen synchronisieren (A dann B, B dann A). Das Ergebnis sollte vorhersagbar sein.
- Testen Sie harte Konflikte: Löschen vs. Edit und Edit vs. Edit auf unterschiedlichen Feldern.
- Machen Sie Zustände sichtbar: Synced, Pending upload und Needs review.
Prototypen Sie den kompletten Ablauf mit einem echten Formular, nicht nur einer Demo-Seite. Nutzen Sie ein realistisches Szenario: Ein Techniker aktualisiert eine Job-Notiz auf dem Telefon, während ein Dispatcher denselben Job-Titel auf einem Tablet ändert. Treffen sie unterschiedliche Felder, auto-merge und eine kleine "Updated from another device"-Hinweis. Treffen sie dasselbe Feld, führen Sie zu einem einfachen Review-Screen mit zwei Optionen und einer klaren Vorschau.
Wenn Sie bereit sind, die mobile App und die Backend-APIs zusammen zu bauen, kann AppMaster (appmaster.io) helfen. Sie können Daten modellieren, Business-Logik definieren und Web- sowie native Mobile-UIs an einem Ort erstellen, dann deployen oder Quellcode exportieren, sobald Ihre Sync-Regeln solide sind.
FAQ
Ein Konflikt entsteht, wenn zwei Geräte denselben serverbasierten Datensatz verändern, während sie offline sind (oder bevor einer von beiden synchronisiert hat). Später erkennt der Server, dass beide Änderungen auf einer älteren Version basieren, und das System muss für jedes Feld entscheiden, welcher Wert der endgültige sein soll.
Beginnen Sie meistens mit einem feldbasierten Merge für Geschäftsanwendungen, weil unterschiedliche Rollen oft verschiedene Felder bearbeiten und Sie so beide Änderungen behalten können, ohne Nutzer zu stören. Verwenden Sie manuelle Überprüfung nur für Felder mit hohem Risiko (Geld, Genehmigungen, Compliance). Last write wins ist nur für niedriges Risiko geeignet, wo das Verwerfen älterer Änderungen akzeptabel ist.
Wenn zwei Änderungen unterschiedliche Felder betreffen, können Sie in der Regel automatisch zusammenführen, ohne den Nutzer zu fragen. Wenn zwei Änderungen dasselbe Feld unterschiedlich setzen, sollte dieses Feld eine Entscheidung auslösen — automatische Entscheidungen können überraschend sein. Beschränken Sie die Entscheidung auf die tatsächlich betroffenen Felder, nicht auf das gesamte Formular.
Behandeln Sie version als den monoton wachsenden Zähler des Servers für einen Datensatz und verlangen Sie vom Client ein expected_version bei jedem Update. Wenn die Server-Version nicht übereinstimmt, lehnen Sie das Update mit einer Konflikt-Antwort ab statt einfach zu überschreiben. Diese Regel verhindert stille Datenverluste, selbst wenn zwei Geräte in unterschiedlicher Reihenfolge synchronisieren.
Mindestens: eine stabile id, eine serverseitig kontrollierte version sowie updated_at/updated_by vom Server, damit nachvollziehbar ist, was sich geändert hat. Auf dem Gerät sollten Sie Felder wie pending_sync und die zuletzt synchronisierte Server-Version speichern. Ohne diese Metadaten können Sie Konflikte nicht zuverlässig erkennen oder auflösen.
Senden Sie nur die Felder, die sich geändert haben (einen Patch), plus die Basis-expected_version. Vollständige Datensatz-Uploads verwandeln kleine, nicht überlappende Änderungen in unnötige Konflikte und erhöhen das Risiko, neuere Serverwerte mit veralteten Daten zu überschreiben. Patches machen außerdem klarer, welche Felder Merge-Regeln brauchen.
Ein Snapshot ist einfacher: Sie speichern den vollständigen Datensatz und vergleichen später mit dem Server. Ein Change-Log ist flexibler: Sie speichern Operationen wie “Feld setzen” oder “Notiz anhängen” und spielen diese auf dem aktuellen Serverzustand ab — das merge-verhalten ist dann oft besser für Notizen, Tags und additive Updates. Wählen Sie Snapshots für schnelle Implementierung; Change-Logs, wenn viele Merges auftreten und Sie genau sehen wollen, wer was geändert hat.
Legen Sie vorher fest, ob ein Löschen stärker ist als eine Bearbeitung. In vielen Business-Apps ist es sinnvoll, Löschen als Tombstone zu behandeln (deleted_at, optional deleted_by und delete_version), damit ein älteres Offline-Upsert den Datensatz nicht wiederbelebt. Wenn Rückgängig wichtig ist, nutzen Sie besser einen Archiv-Status statt eines harten Löschens.
Verlassen Sie sich nicht auf Gerätezeit, um Reihenfolgen zu bestimmen — Uhren laufen falsch und Zeitzonen ändern sich. Nutzen Sie serverseitige Versionen zur Reihenfolge und Konfliktprüfung. Vermeiden Sie Last-write-wins bei sensiblen Feldern wie Status, Zuweisung oder Summen; geben Sie diesen Feldern explizite Regeln oder erfordern Sie eine manuelle Überprüfung. Zeigen Sie nur die tatsächlich betroffenen Felder in der Konflikt-UI, nicht den gesamten Datensatz.
Halten Sie das Versprechen, dass „Saved“ lokal gespeichert ist und machen Sie „Synced“ zu einem separaten, sichtbaren Zustand. Wenn Sie das in AppMaster bauen, definieren Sie pro Feld Merge-Regeln, führen Sie sichere Felder automatisch zusammen und zeigen Sie eine fokussierte Überprüfungsseite nur bei echten Feldkollisionen. Testen Sie mit zwei Geräten, die denselben Datensatz offline bearbeiten und in beiden Reihenfolgen synchronisieren.


