Vom Praxisproblem zum belastbaren System.

Bohris ist eine Vertical-SaaS-Plattform für Erdwärmebohrfirmen. Diese Case Study zeigt, wie aus einem neunstufigen Branchenworkflow ein offline-fähiges, mandantenfähiges System wurde — von Product Discovery und UX bis zu Rust-Services, Betrieb, Observability und Testautomatisierung.

Verantwortung
Product Discovery · Architektur · Fullstack-Umsetzung · Betrieb
Produktsystem
Neunstufiger Workflow · Web · iOS · Android
Flutter-Client Web · iOS · Android
Riverpod Drift / SQLite Offline Queue
API-Grenze Gateway Auth · Rollen · HTTP / gRPC
Rust-Services Fachliche Module
PostgreSQL NATS JetStream
Lokaler Arbeitsstand auf der Baustelle, klar definierte API-Grenzen und ereignisbasierte Backend-Prozesse.

Die eigentliche Aufgabe ist der Übergang.

Ein digitales Formular allein löst den Arbeitsalltag einer Bohrfirma nicht. Kritisch sind die Übergaben zwischen Vertrieb, Behörden, Disposition, Baustelle, Dokumentation und Abrechnung — besonders dann, wenn mehrere Rollen gleichzeitig arbeiten und auf der Baustelle das Netz ausfällt.

Deshalb bildet Bohris nicht einzelne Screens, sondern einen gemeinsamen Projektzustand ab. Jede technische Entscheidung lässt sich auf ein reales Betriebsrisiko zurückführen: Informationsverlust, Doppelerfassung, unklare Verantwortung oder nicht synchronisierte Felddaten.

  1. Auslegung
  2. Angebot
  3. Antrag
  4. Erlaubnis
  5. Disposition
  6. Bohren
  7. Anbinden
  8. Dokumentation
  9. Abrechnung

Architektur folgt den Risiken.

Der Stack ist kein Selbstzweck. Jede Grenze beantwortet eine konkrete Frage aus Produkt, Betrieb oder Zuverlässigkeit.

Was passiert im Funkloch?

Lokale Datenbank statt Online-Zwang.

Drift und SQLite halten den Arbeitsstand auf dem Gerät. Schreibvorgänge landen in einer Offline-Queue und werden mit Idempotenz- und Konfliktregeln synchronisiert, sobald wieder Verbindung besteht.

Trade-off: Mehr Zustandslogik im Client — dafür bleibt der Kernprozess auf der Baustelle arbeitsfähig und Felddaten werden nicht still verworfen.

Wie bedient ein System sechs unterschiedliche Rollen?

Eine Codebasis, rollenbasierte Oberflächen.

Flutter liefert Web-, Tablet- und Mobile-Oberflächen aus einer Codebasis. Riverpod strukturiert den Zustand; GoRouter trennt die Rollen-Shells für Geschäftsführung, Vertrieb, Operations, Finance, Baustelle und Supply.

Trade-off: Gemeinsamer Code verlangt strikte Modul- und Design-System- Grenzen — dafür bleiben Domänenlogik und Interaktion konsistent.

Wo endet eine fachliche Verantwortung?

Gateway plus klar geschnittene Rust-Services.

Das Gateway bündelt Authentifizierung, Rollenprüfung und externe HTTP-Schnittstellen. Fachliche Services kommunizieren über explizite Protobuf-/gRPC-Verträge und besitzen ihre jeweilige Persistenzlogik.

Trade-off: Service-Grenzen erhöhen Betriebsaufwand. Sie werden nur dort eingesetzt, wo fachliche Ownership und unabhängige Fehlergrenzen den Aufwand rechtfertigen.

Wie bleiben Ereignisse trotz Teilausfällen verlässlich?

Transaktionen, Outbox und langlebige Events.

PostgreSQL hält den fachlichen Zustand. Eine Transactional Outbox koppelt Zustandsänderungen sicher an NATS JetStream, damit ein vorübergehend nicht erreichbarer Broker keine fachlichen Ereignisse verschwinden lässt.

Trade-off: At-least-once-Zustellung erfordert idempotente Consumer — dafür wird ein Netz- oder Brokerfehler nicht zum Datenverlust.

Offline heißt weiterarbeiten, nicht nur ansehen.

Der Offline-Pfad ist ein eigener Produktfluss mit sichtbaren Zuständen — kein Cache, der bei der ersten Änderung an seine Grenze kommt.

  1. 1 Lokal erfassen

    Bohrdaten und Protokolle werden zuerst auf dem Gerät gespeichert.

  2. 2 Sicher einreihen

    Änderungen erhalten eine stabile Reihenfolge und Idempotenz-Information.

  3. 3 Gezielt synchronisieren

    Nach dem Reconnect werden Deltas übertragen und bestätigt.

  4. 4 Konflikte sichtbar lösen

    Widersprüche werden persistiert und explizit behandelt statt überschrieben.

Zuverlässigkeitsregel: Ein Sync-Konflikt darf Felddaten niemals lautlos überschreiben.

Der technische Schnitt.

Die Komponenten sind so gewählt, dass sie den jeweiligen Betriebsfall tragen — nicht damit die Technologieliste möglichst lang wird.

Ebene Technik Verantwortung
Client Flutter · Riverpod · GoRouter Rollenbasierte Oberflächen und konsistenter App-Zustand
Offline Drift · SQLite · Sync Queue Lokale Schreibfähigkeit, Wiederholung und Konfliktpersistenz
API Dio · Gateway · Protobuf / gRPC Authentifizierung, Rollen, externe und interne Verträge
Backend Rust · Axum · Tonic · SQLx Fachlogik, Validierung und transaktionale Persistenz
Daten & Events PostgreSQL · NATS JetStream Mandantenfähiger Zustand und langlebige Fachereignisse
Betrieb Docker · Prometheus · Grafana · Loki · GlitchTip Deployment, Metriken, Logs, Alarmierung und Fehleranalyse

Qualität ist Teil der Architektur.

Die kritischen Pfade werden auf mehreren Ebenen geprüft. Schnelle Tests sichern Logik und UI-Zustand; echte Integrations- und E2E-Flows prüfen die Grenzen, an denen Systeme in der Praxis auseinanderfallen.

Unit · Provider · Widget Domänenlogik, Riverpod-Zustand und Interaktion
Contract · Integration API-Verträge, echte Datenbank und Service-Grenzen
Patrol E2E Rollenflüsse, Offline-Tag und kritische Nutzerpfade
CI-Gates Formatierung, Analyse, Custom Lints und Coverage-Ratchet

Fachinput und technische Verantwortung sind klar getrennt.

Branchenwissen liefert die Probleme und Randbedingungen. Meine Aufgabe ist es, daraus überprüfbare Produkt- und Systementscheidungen zu machen.

Praxis-Input

Kontinuierliches Feedback aus der Bohrpraxis beschreibt Pain Points, Sprache, Ausnahmen und die Folgen fehlerhafter Übergaben.

Product & Engineering

Ich verantworte Discovery, Priorisierung, Domänenmodell, UX, Architektur, Frontend, Backend, Tests und Betrieb — einschließlich der Entscheidung, was bewusst nicht gebaut wird.

Entscheidungen werden an ihren Folgen messbar.

Entscheidend ist die Verbindung von Fachproblem, Trade-off und überprüfbarem Ergebnis: Offline-Fähigkeit führt zu lokaler Persistenz und Konfliktmodellen; Zuverlässigkeit zu Idempotenz, Outbox und Observability; mehrere Rollen zu klaren Modulen und gemeinsamen Qualitätsstandards.