Zur Serienübersicht
Projektserie04 / 04

Implementierung · 12 min · 2026-09

Von der Architektur zur ersten funktionierenden Anwendung

Die Architektur steht. Jetzt beginnt die eigentliche Umsetzung. Ich richte die technische Grundlage von FlowOps ein, verbinde Next.js mit Cloudflare Workers und D1, stoße dabei auf eine veränderte Cloudflare-Empfehlung rund um Hono und vinext und dokumentiere die ersten technischen Probleme genauso wie die Lösungen.

Von der Architektur zur ersten funktionierenden Anwendung

Im letzten Beitrag habe ich die technische Architektur von FlowOps definiert.

Damit war geklärt, welche Technologien ich verwenden möchte, wie die Anwendung grundsätzlich aufgebaut sein soll und welche Anforderungen die Architektur erfüllen muss.

Aber eine Architektur auf Papier ist noch keine Anwendung.

Jetzt stellt sich deshalb die nächste Frage:

Wie bringe ich diese Architektur in ein tatsächlich laufendes Projekt?

Genau an diesem Punkt beginnt für mich der interessanteste Teil des Projekts. Denn ab jetzt geht es nicht mehr nur darum, Entscheidungen zu treffen und Diagramme zu zeichnen. Die Entscheidungen müssen sich in der Praxis bewähren.

Und dabei wird sich auch zeigen, dass nicht jede Entscheidung aus der Planung exakt so in die Implementierung übernommen werden kann.


Von der Architektur zur Implementierung

Bevor ich direkt mit dem ersten Code begonnen habe, wollte ich zunächst die Implementierung selbst strukturieren.

Dafür habe ich im Projekt eine eigene Planungsebene angelegt:

docs/
└── Planning/
    └── 04-Implementation/

Dort liegen die Dokumente, die den Übergang von der Architektur zur eigentlichen Entwicklung beschreiben.

Ein wichtiger Bestandteil davon ist die Definition-of-Ready.md.

Sie beschreibt, wann eine Aufgabe ausreichend vorbereitet ist, um überhaupt in die Umsetzung zu gehen. Für mich ist das eine wichtige Unterscheidung: Nur weil eine Idee existiert, ist sie noch lange nicht bereit, implementiert zu werden.

Daneben gibt es die Definition-of-Done.md.

Hier geht es um die andere Seite des Problems: Wann kann ich eine Aufgabe tatsächlich als abgeschlossen betrachten?

Dazwischen liegt die eigentliche Planung. In planung.md habe ich die technischen Arbeiten konkreter aufgeteilt und mit sprints.md die Umsetzung in einzelne Sprints strukturiert.

Die Struktur sieht damit vereinfacht so aus:

Architektur
    ↓
Implementierungsplanung
    ↓
Definition of Ready
    ↓
Sprint
    ↓
Implementierung
    ↓
Definition of Done

Dabei war mir von Anfang an wichtig, Planung und Realität nicht miteinander zu verwechseln.

Eine Planung ist eine Ausgangsbasis.

Sobald ich beginne, tatsächlich zu implementieren, können neue Informationen auftauchen. Eine Abhängigkeit funktioniert anders als erwartet. Eine Plattform verändert ihre Empfehlung. Eine technische Entscheidung erzeugt mehr Aufwand als ursprünglich angenommen.

Dann muss die Planung angepasst werden.

Für mich ist das kein Zeichen für eine schlechte Planung, sondern ein normaler Bestandteil von Softwareentwicklung.

Ich möchte deshalb auch diesen Teil der Entwicklung dokumentieren und nicht erst am Ende eine aufgeräumte Geschichte erzählen, in der alles von Anfang an genauso geplant war.

Der tatsächliche Ablauf sieht eher so aus:

Planung
   ↓
Implementierung
   ↓
Problem oder neue Erkenntnis
   ↓
Entscheidung
   ↓
Anpassung
   ↓
Ergebnis

Genau das sollte sich bereits im ersten Sprint zeigen.


Sprint 0 beginnt

Der erste Sprint trägt deshalb bewusst noch keine fachliche Funktionalität.

Ich nenne ihn Sprint 0.

Das Ziel war:

Eine leere, aber technisch funktionierende Projektbasis, auf der die folgenden Sprints umgesetzt werden können.

Das klingt zunächst unspektakulär.

Für mich war dieser Schritt aber wichtig.

Ich wollte nicht gleichzeitig Framework, Datenbank, Authentifizierung, API, Benutzeroberfläche und Geschäftslogik entwickeln und erst am Ende herausfinden, dass die technische Grundlage nicht funktioniert.

Stattdessen wollte ich zunächst die Basis zum Laufen bekommen.

Dazu gehören:

  • Repository und Projektstruktur
  • Entwicklungsumgebung
  • grundlegende Projektkonfiguration
  • Abhängigkeiten und Build
  • lokale und produktionsnahe Umgebung
  • grundlegende Code-Struktur
  • Grundlage für spätere Tests und CI
  • Dokumentation der technischen Entscheidungen

Noch einmal ausdrücklich: Sprint 0 implementiert keine FlowOps-Funktionalität.

Es gibt zu diesem Zeitpunkt noch keine Incidents, keine Benutzerverwaltung, keine Authentifizierung und keine fertige REST-API.

Das Ziel ist zunächst viel einfacher:

Kann ich FlowOps überhaupt bauen,
starten,
bauen
und in der vorgesehenen Umgebung ausführen?

Eine erste Abweichung von der Planung

Genau hier hat sich bereits eine wichtige Sache verändert.

In der ursprünglichen technischen Planung hatte ich Hono als Backend-Framework vorgesehen.

Das war zu diesem Zeitpunkt eine nachvollziehbare Entscheidung. Hono ist leichtgewichtig und passt sehr gut zu Cloudflare Workers. Cloudflare dokumentiert Hono weiterhin ausdrücklich als Framework für Workers und zeigt auch die direkte Verwendung von D1 über die Worker-Bindings.

Dann hat sich während meiner Vorbereitung allerdings die Situation rund um Next.js auf Cloudflare verändert.

Cloudflare hat den empfohlenen Weg für Next.js auf Workers weiterentwickelt und empfiehlt inzwischen vinext als Standardweg für Next.js-Anwendungen auf Cloudflare Workers. vinext befindet sich zwar noch in der Beta-Phase, unterstützt aber unter anderem den App Router, Route Handler, React Server Components und Cloudflare Bindings.

Damit musste ich eine Entscheidung aus meiner ursprünglichen Planung noch einmal überprüfen.

Nicht weil Hono plötzlich schlecht geworden wäre.

Sondern weil sich die Rahmenbedingungen geändert hatten.

Die ursprüngliche Überlegung sah vereinfacht so aus:

Next.js
   │
   └── Hono
        │
        └── Cloudflare Workers
             │
             └── D1

Jetzt stellte sich die Frage, ob ich tatsächlich eine zusätzliche Backend-Schicht brauche, wenn Next.js selbst über vinext auf Cloudflare Workers betrieben werden kann.

Also habe ich die neue Möglichkeit nicht einfach theoretisch bewertet, sondern ausprobiert.

Zuerst habe ich mit

npx vinext check

geprüft, wie kompatibel das bestehende Projekt ist.

Anschließend habe ich vinext init verwendet und die bestehende Next.js-Struktur auf die neue Laufzeit vorbereitet.

Das war für mich ein wichtiger Moment im Projekt.

Denn hier zeigt sich genau, warum ich Planung und Implementierung getrennt dokumentiere.

Die Architekturplanung war nicht wertlos.

Sie hat mir einen Ausgangspunkt gegeben.

Aber die Implementierung hat neue Informationen geliefert.

Und diese Informationen mussten in die Entscheidung einfließen.

Die aktuelle Richtung sieht deshalb zunächst so aus:

Next.js
   │
   └── vinext / Vite
        │
        └── Cloudflare Workers
             │
             └── D1

Hono ist damit nicht grundsätzlich aus meinem technischen Werkzeugkasten verschwunden. Die Entscheidung betrifft zunächst die konkrete Implementierung von FlowOps.

Ich möchte aber auch nicht behaupten, dass diese Entscheidung für das gesamte Projekt bereits für alle zukünftigen Sprints endgültig festgeschrieben ist.

Wenn die weitere Implementierung zeigt, dass eine zusätzliche API-Schicht sinnvoll wird, kann diese Entscheidung erneut überprüft werden.

Genau das ist für mich der Unterschied zwischen Architektur und Dogma.


Das erste technische Ziel: ein laufender Worker

Nachdem diese Entscheidung getroffen war, konnte ich mich auf das eigentliche Ziel von Sprint 0 konzentrieren.

Ich wollte zunächst eine minimale Next.js-Anwendung aufbauen, die über vinext als Cloudflare Worker ausgeführt werden kann.

Dabei wurde aus dem ursprünglichen Next.js-Projekt Schritt für Schritt eine Kombination aus:

Next.js
React
   ↓
vinext / Vite
   ↓
Cloudflare Worker
   ↓
Cloudflare D1

Die Cloudflare-Konfiguration liegt in wrangler.jsonc.

Dort wird unter anderem die D1-Datenbank als Binding definiert.

Das ist wichtig, weil die Anwendung später nicht direkt über eine Datenbank-URL oder ein Passwort auf D1 zugreifen soll.

Stattdessen stellt die Workers-Laufzeit das Binding bereit.

In der aktuellen Implementierung kann Server-Code deshalb über

cloudflare:workers

auf die Umgebung zugreifen. Genau diesen Mechanismus dokumentiert Cloudflare auch für vinext-Anwendungen.

Damit war die nächste Frage klar:

Funktioniert nicht nur der Worker, sondern auch die Verbindung zur Datenbank?


Die Datenbank kommt dazu

Für FlowOps hatte ich bereits in der Architektur Cloudflare D1 als Datenbank vorgesehen.

Jetzt wurde es Zeit, diese Entscheidung praktisch zu überprüfen.

Zunächst habe ich die D1-Datenbank flowops-db angelegt und sie als flowops_db an den Worker gebunden.

Danach habe ich die lokale D1-Umgebung getestet.

Der erste Test war bewusst minimal:

SELECT 1 AS ok;

Die Antwort war:

ok
--
1

Das ist natürlich noch keine Anwendung.

Aber es beantwortet eine wichtige technische Frage:

Kann meine lokale Entwicklungsumgebung tatsächlich mit einer D1-Datenbank kommunizieren?

Die Antwort war ja.

Damit konnte der nächste Schritt folgen.


Das erste Datenbankschema

Als Nächstes habe ich die erste Migration angelegt:

migrations/
└── 0001_initial_schema.sql

Darin befindet sich das erste FlowOps-Datenbankschema.

Die Migration wurde anschließend lokal ausgeführt.

Dabei wurden die vorgesehenen Tabellen angelegt:

organizations
users
organization_members
sessions
services
incidents
incident_events
comments
postmortems

Damit existiert zum ersten Mal die technische Grundlage für die späteren fachlichen Bereiche von FlowOps.

Aber auch hier ist mir die Abgrenzung wichtig:

Die Tabellen existieren. Die Fachlogik, die sie verwendet, existiert noch nicht.

Es gibt also noch keine funktionierende Incident-Verwaltung.

Das Datenbankschema ist zu diesem Zeitpunkt lediglich die Grundlage dafür.

Ich habe anschließend auch überprüft, ob die Migration tatsächlich alle erwarteten Tabellen angelegt hat.

Die lokale Datenbank konnte das Schema korrekt laden.

Damit war der Datenbankteil von Sprint 0 technisch grundsätzlich funktionsfähig.


Ein Health Check als kleinster gemeinsamer Nenner

Jetzt hatte ich zwei technische Komponenten:

Cloudflare Worker
       │
       └── D1

Was mir noch fehlte, war ein einfacher Nachweis, dass die Anwendung beide Komponenten tatsächlich miteinander verbindet.

Dafür habe ich bewusst keine komplizierte API gebaut.

Stattdessen gibt es zunächst nur:

GET /api/health

Der Endpoint führt eine minimale Datenbankabfrage aus:

SELECT 1 AS ok

und gibt anschließend den Zustand zurück.

Bei erfolgreicher Verbindung:

{
  "status": "ok",
  "database": "connected"
}

Damit konnte ich einen sehr kleinen, aber wichtigen technischen Pfad überprüfen:

HTTP Request
     ↓
Next.js Route Handler
     ↓
vinext
     ↓
Cloudflare Worker
     ↓
D1 Binding
     ↓
SQL Query
     ↓
JSON Response

Genau diese Kette wollte ich in Sprint 0 einmal vollständig durchlaufen.

Und sie funktioniert.


Vom Development Server zum Worker

An dieser Stelle gab es noch einen wichtigen Unterschied.

Es reicht mir nicht, wenn eine Anwendung nur über den normalen Entwicklungsserver funktioniert.

Die spätere Zielumgebung ist Cloudflare Workers.

Deshalb habe ich zusätzlich den vinext-Produktionsbuild ausgeführt.

Der Build lief erfolgreich durch und erkannte unter anderem den /api/health-Endpoint als API-Route.

Anschließend habe ich auch den erzeugten Worker lokal über Wrangler gestartet.

Dabei wurde die lokale D1-Datenbank als Binding eingebunden.

Der Worker lief anschließend unter:

http://127.0.0.1:8787

Auch dort habe ich /api/health aufgerufen.

Das Ergebnis war erneut:

{
  "status": "ok",
  "database": "connected"
}

Damit war für mich der wichtigste technische Nachweis von Sprint 0 erbracht.

Nicht nur:

"Der Code lässt sich starten."

sondern:

Next.js
   ↓
vinext
   ↓
Cloudflare Worker
   ↓
D1
   ↓
SQL
   ↓
HTTP Response

funktioniert tatsächlich.

Das ist ein kleiner Schritt.

Aber auf dieser Basis kann der Rest des Projekts jetzt aufgebaut werden.


Das erste Problem mit der Qualitätssicherung

Während der technischen Einrichtung ist noch ein anderes Problem aufgetreten.

Ich hatte ESLint eingerichtet und wollte den Code mit

npm run lint

prüfen.

Beim ersten Lauf war das Ergebnis allerdings alles andere als sauber.

ESLint meldete insgesamt 2483 Probleme.

Davon waren:

  • 3 Fehler
  • 2480 Warnungen

Das Problem lag dabei nicht hauptsächlich in meinem eigentlichen Anwendungscode.

ESLint analysierte unter anderem erzeugte Dateien aus dem Build-Verzeichnis und generierte Cloudflare-Typdefinitionen.

Das war ein gutes Beispiel dafür, warum ich einen Quality Check nicht einfach nur deshalb als erfolgreich betrachten möchte, weil ein Tool installiert ist.

Das Tool hat korrekt gearbeitet.

Meine Konfiguration war nur noch nicht passend zur tatsächlichen Projektstruktur.

Ich habe deshalb die generierten Verzeichnisse und Dateien in der ESLint-Konfiguration explizit ausgeschlossen.

Danach lief:

npm run lint

sauber durch.

Für mich war das eine kleine, aber wichtige Erkenntnis: Qualitätssicherung beginnt nicht erst mit den eigentlichen Tests.

Auch die Werkzeuge selbst müssen zur Build- und Entwicklungsumgebung passen.


TypeScript und Build

Neben ESLint habe ich anschließend auch den TypeScript-Compiler ausgeführt:

npx tsc --noEmit

Auch dieser Check war erfolgreich.

Danach folgte erneut der vollständige vinext-Build:

npm run build:vinext

Auch dieser Build lief erfolgreich durch.

Damit hatte ich inzwischen drei unterschiedliche Ebenen überprüft:

Lint
  ↓
Typecheck
  ↓
Build

und anschließend zusätzlich:

Worker starten
  ↓
API aufrufen
  ↓
D1 abfragen

Das ist noch keine umfassende Qualitätssicherung.

Aber es ist eine funktionierende technische Basis.


Warum es noch keine automatisierten Tests gibt

Ein Punkt, den ich bewusst nicht schöner darstellen möchte, als er aktuell ist:

Es gibt momentan noch keine echte Testsuite.

Das Verzeichnis

tests/

existiert bereits, enthält aber noch keine automatisierten Tests.

Das ist Absicht.

Ich wollte nicht künstlich irgendwelche Tests schreiben, nur damit CI bereits einen grünen Testschritt anzeigen kann.

Die fachliche Logik von FlowOps existiert zu diesem Zeitpunkt noch gar nicht.

Es gibt deshalb auch noch keinen sinnvollen Umfang an Unit-, Integrations- oder End-to-End-Tests, den ich hier seriös abdecken könnte.

Die umfassendere Qualitätssicherung ist für den nächsten Planungsschritt vorgesehen.

Das ist für mich ein wichtiger Unterschied:

Technische Grundlage funktioniert
            ≠
Anwendung ist vollständig getestet

Sprint 0 soll die erste Aussage beweisen.

Die zweite kommt später.


Continuous Integration

Trotzdem wollte ich bereits jetzt sicherstellen, dass grundlegende technische Fehler nicht ausschließlich lokal bei mir entdeckt werden.

Dafür habe ich GitHub Actions eingerichtet.

Die aktuelle CI-Pipeline ist bewusst klein:

Push / Pull Request
        ↓
   npm ci
        ↓
      Lint
        ↓
   Typecheck
        ↓
      Build

Der Workflow läuft auf GitHub Actions und verwendet Node.js 24.

Auch hier habe ich bewusst noch keinen Testschritt ergänzt.

Nicht weil Tests unwichtig wären, sondern weil momentan noch keine Tests vorhanden sind.

Die Pipeline soll den tatsächlichen Zustand des Projekts abbilden und nicht einen künstlichen Reifegrad suggerieren.


Was am Ende von Sprint 0 tatsächlich vorhanden ist

Nach diesen Schritten sieht die technische Basis von FlowOps inzwischen deutlich anders aus als zu Beginn.

Vor Sprint 0 hatte ich vor allem:

Anforderungen
    ↓
MVP
    ↓
Architektur
    ↓
Planung

Jetzt gibt es zusätzlich eine laufende technische Basis:

Next.js
   ↓
vinext / Vite
   ↓
Cloudflare Worker
   ↓
Cloudflare D1
   ↓
Migration
   ↓
Health API

Dazu kommen:

TypeScript
ESLint
Git
GitHub
GitHub Actions
Wrangler

und die ersten Implementierungsregeln über:

Definition of Ready
Definition of Done
Implementierungsplanung
Sprintplanung

Das ist noch kein Incident-Management-System.

Aber es ist ein Projekt, auf dem eines entstehen kann.


Was noch nicht implementiert ist

Gerade bei einem Projekt wie FlowOps finde ich es wichtig, auch die fehlenden Dinge ausdrücklich zu nennen.

Aktuell noch nicht vorhanden

  • [ ] Benutzerregistrierung
  • [ ] Authentifizierung
  • [ ] Session-Logik
  • [ ] Rollen- und Rechteverwaltung
  • [ ] Incident-Erstellung
  • [ ] Incident-Lifecycle
  • [ ] Service-Verwaltung
  • [ ] Kommentare
  • [ ] Postmortems
  • [ ] fachliche API
  • [ ] vollständige Benutzeroberfläche
  • [ ] automatisierte Test-Suite
  • [ ] produktive Deployment-Pipeline

Auch die geplante Authentifizierung aus der Architektur gehört ausdrücklich noch nicht zu diesem Sprint.

Sie ist für einen späteren Sprint vorgesehen.

Das Gleiche gilt für die umfassendere Qualitätssicherung und den Release-Prozess.

Ich möchte diese Grenzen bewusst sichtbar halten.

Denn ein funktionierender /api/health-Endpoint bedeutet nicht, dass FlowOps bereits einsatzbereit ist.


Der erste Commit

Nachdem die technische Grundlage stand und die lokalen Prüfungen erfolgreich waren, habe ich den Stand als ersten klaren Entwicklungspunkt festgehalten.

Der Commit lautet:

c9188f9 chore: establish Sprint 0 technical foundation

Der Stand wurde anschließend auf GitHub gepusht.

Damit ist die technische Basis jetzt nicht mehr nur auf meinem lokalen Rechner vorhanden, sondern auch versioniert im Repository.

Das klingt vielleicht banal.

Für mich markiert dieser Commit aber einen klaren Übergang:

Planung
   ↓
lokale Umsetzung
   ↓
technische Überprüfung
   ↓
versionierter Entwicklungsstand

Ab diesem Punkt kann die eigentliche Entwicklung von FlowOps beginnen.


Was ich aus Sprint 0 mitnehme

Sprint 0 war fachlich noch sehr klein.

Technisch war er für mich trotzdem wichtig.

Die wichtigste Erkenntnis ist wahrscheinlich, dass sich die Implementierung nicht vollständig aus der Architektur ableiten lässt.

Die Architektur beschreibt eine Richtung.

Erst die Umsetzung zeigt, ob diese Richtung mit den aktuellen Werkzeugen und Rahmenbedingungen tatsächlich sinnvoll funktioniert.

Das hat sich bei der Entscheidung rund um Hono und vinext bereits gezeigt.

Die ursprüngliche Planung war eine gute Ausgangsbasis.

Dann haben sich die Rahmenbedingungen verändert.

Ich habe die neue Möglichkeit geprüft, ausprobiert und die Implementierungsrichtung angepasst.

Genau deshalb möchte ich FlowOps nicht nur als fertiges Portfolio-Projekt dokumentieren.

Mich interessiert auch der Weg dorthin.

Dazu gehören kleine Probleme wie eine falsch konfigurierte ESLint-Ausnahme genauso wie größere technische Entscheidungen.

Denn gerade diese Dinge zeigen, wie Softwareentwicklung tatsächlich funktioniert.

Nicht:

Plan → Code → fertig

sondern eher:

Plan
  ↓
Umsetzen
  ↓
Überprüfen
  ↓
Problem entdecken
  ↓
Entscheidung treffen
  ↓
Anpassen
  ↓
Erneut überprüfen

Und genau diesen Prozess möchte ich bei FlowOps weiterführen.


Was kommt als Nächstes?

Mit Sprint 0 ist die technische Grundlage von FlowOps geschaffen. Das Projekt lässt sich lokal starten, die Anwendung wird über vinext als Cloudflare Worker ausgeführt und die Verbindung zur D1-Datenbank funktioniert. Auch der erste CI-Workflow ist eingerichtet.

Gleichzeitig ist wichtig, was dieser Stand noch nicht bedeutet: FlowOps ist damit noch keine funktionierende Incident-Management-Plattform. Es gibt noch keine Benutzeroberfläche für die eigentlichen Anwendungsfälle, keine Authentifizierung, keine Incident-Logik und noch keine vollständige Testabdeckung.

Der nächste Schritt wird deshalb darin bestehen, die Qualitätssicherung systematischer aufzubauen. Bevor weitere fachliche Funktionen entstehen, möchte ich mich damit beschäftigen, wie ich sicherstelle, dass die kommenden Änderungen zuverlässig funktionieren und Fehler möglichst früh erkannt werden.

Diesen Teil der Planung habe ich zum aktuellen Zeitpunkt noch nicht als fertigen Bereich im Repository angelegt. Genau das gehört für mich aber ebenfalls zum Entwicklungsprozess: Nicht alles, was irgendwann geplant ist, existiert bereits als fertige Struktur.

Im nächsten Beitrag geht es deshalb darum, wie ich aus der bisher eher technischen Grundlage einen belastbaren Qualitätsprozess für FlowOps entwickle.

Bis dahin ist der wichtigste Schritt geschafft:

Aus einer Architektur auf dem Papier ist ein tatsächlich laufendes Projekt geworden.