Frontend-Architektur · Inkrementelle Migration · 2026 · gemessen Juni bis September

Eine schrittweise Migration von Razor zu React leiten, ohne ein produktives Produkt anzuhalten

Das Produkt war eine produktive mandantenfähige ASP.NET-Core-Plattform, deren Oberfläche über MVC, Razor, JavaScript/jQuery und einige Blazor-Komponenten gewachsen war. Ein Neuschreiben war nicht realistisch, also habe ich die Frontend-Richtung einer inkrementellen Migration zu React + TypeScript verantwortet: hybride Navigation, von Features besessene Routen, ein Same-Origin-BFF, eine wiederverwendbare Seitenstruktur und Build-Absicherungen, damit Legacy-Oberfläche und React nebeneinander bestehen konnten, bis jeder Fachbereich sicher ersetzt werden konnte.

Meine Rolle
Frontend-Architektur und Umsetzungsrichtung: meine. Die fachlichen Features auf der Plattform: das ganze Team.
Stack
  • React
  • TypeScript
  • React Router
  • Redux Toolkit
  • RTK Query
  • Vite (library mode)
  • ASP.NET Core
  • BFF

Auf einen Blick

  • Echte React-Routen wuchsen zwischen der Juni-Baseline und dem September-Snapshot von 10 auf 112
  • JavaScript-Einstiegsartefakt von 1'764'861 auf 63'249 Bytes (nur der Einstiegs-Chunk)
  • 108 echte Routen auf der Staging-Linie, davon 65 explizit auf Legacy-URLs abgebildet

Kontext

Das Produkt war eine produktive, mandantenfähige ASP.NET-Core-Plattform, deren Oberfläche über MVC, Razor, JavaScript/jQuery und einzelne Blazor-Komponenten gewachsen war. Ein vollständiges Neuschreiben war nicht realistisch: Kunden hingen von der bestehenden Anwendung ab, die Feature-Entwicklung musste weiterlaufen, und viele Legacy-Bereiche waren mit Authentifizierung, Mandantenkontext und operativen Abläufen gekoppelt.

Die Randbedingungen waren konkret. Das bestehende Produkt musste durchgehend nutzbar bleiben. Legacy- und React-Navigation mussten nebeneinander funktionieren, ohne Autorisierung oder Deep Links zu brechen. Der Browser durfte das Bearer-Token der nachgelagerten API nie erhalten. Neue Features brauchten wiederholbare Muster für State, API-Verträge, Layout und Fehlerbehandlung. Und jede Änderung durchlief Testing und Staging vor der Produktion, musste also kompatibel und leicht zurücknehmbar sein.

Was ich getan habe

  • React und TypeScript unter /app im bestehenden ASP.NET-Host eingebunden, mit React Router und /app als Basename, während ASP.NET Session und serverseitige Integration weiter verantwortete.
  • Den Fluss Browser → Same-Origin-BFF aufgesetzt: Das API-JWT bleibt in der serverseitigen Session, React erhält einen Authentifizierungs-Snapshot und nutzt CSRF-Schutz für Mutationen. Redux Toolkit und RTK Query wurden zum gemeinsamen Modell für Server-State, Caching und Endpunkt-Injektion pro Feature.
  • Das hybride Navigationsmodell entworfen — Legacy-Seite, automatische Auflösung oder explizite React-Route — mit gemeinsamen Hilfsfunktionen, die Query-Strings und Hashes erhalten und SPA-, Vollseiten- und externe Ziele über eine einzige Schnittstelle behandeln.
  • Die Routen-Erkennung auf von Features besessene Definitionen über import.meta.glob verlagert, mit Duplikatprüfung, sodass ein normales Feature kein zentrales Register mehr bearbeitet.
  • PageShell gebaut, ein wiederverwendbares Layout mit Header, optionalem Subheader und scrollbarem Body: Operative Desktop-Seiten halten Aktionen und Filter sichtbar, während der Datenbereich scrollt; kleine Bildschirme fallen auf natürliches Seitenscrollen zurück.
  • Routen- und Vendor-Code-Splitting im Vite-Library-Mode-Build umgesetzt und dabei die ASP.NET-Brücke erhalten, ein einziges Stylesheet beibehalten (aufgeteiltes CSS konnte in diesem Aufbau verwaist bleiben) und einen Bundle-Prüfer ergänzt, der den Build bei fehlenden referenzierten Chunks scheitern lässt.

Entscheidungen

Inkrementell, im selben Host

Keine zweite Anwendung und kein Big-Bang-Umschalten. React lebt unter /app im bestehenden ASP.NET-Host, sodass jeder Fachbereich wechselt, wenn es sicher ist, und die Legacy-Seite bis dahin verfügbar bleibt.

Das Token bleibt auf dem Server

Der BFF hält das API-JWT in der serverseitigen Session; der Browser erhält immer nur einen Authentifizierungs-Snapshot und ein CSRF-Token für Mutationen.

Routen gehören den Features

Routendefinitionen liegen neben dem Feature und werden zur Build-Zeit erkannt. Das zentrale Register war zum Merge-Brennpunkt geworden und ein leichter Ort, einen Eintrag zu vergessen.

Aufgeteiltes JavaScript, ein Stylesheet

Code-Splitting für Routen und Vendors, aber ein vereinheitlichtes CSS-Bundle: Im Library Mode konnte ein aufgeteiltes Stylesheet nie geladen werden. Der Prüfer fängt einen fehlenden Chunk beim Release ab statt in der Produktion.

Die konkrete Schwierigkeit

Mit wachsender React-Oberfläche wurden BFF und Routing zur Hauptquelle von Integrationsrisiko: auf der einen Seite eine grosse zentrale Endpunktfläche, eine Abhängigkeitsregistrierung, die zwischen Produktion und Tests auseinanderlaufen konnte, geteilte HTTP-Header und Vertragsabweichungen zwischen Client und Server; auf der anderen doppelte Resolver und inkonsistente Ziele.

Stabilisieren bedeutete Endpunkte, Services und DTOs pro Feature mit gemeinsamer Dependency-Injection-Komposition, HTTP-Header pro Anfrage, Inventarprüfungen zwischen Client und Server, Swagger-Vertragsverifikation, zentralisierte Navigation und Bundle-Prüfungen beim Release. Ich habe mit der Person zusammengearbeitet, die Release-Branches befördert, und blieb durch das Staging hindurch beteiligt, bis die Frontend-Fehler ein stabiles operatives Niveau erreichten.

Ergebnis

  • Zwischen der verifizierten Juni-Baseline und dem September-Snapshot wuchsen die echten React-Routen auf HEAD von 10 auf 112; die untersuchte Staging-Linie enthielt 108 echte Routen, ohne 42 Platzhalter und zwei reine Entwicklungsrouten.
  • Von den 108 Staging-Routen waren 65 explizit auf Legacy-URLs abgebildet, 16 als native React-Routen deklariert und 27 zusätzliche Detail-, Erstell-, Bearbeitungs- oder anderweitig nicht abgebildete Pfade.
  • Die nach Staging beförderten React-Seiten waren nach direkter Beobachtung während der Arbeit funktionsfähig; das Repository enthielt 317 Frontend-Testdateien, und der Migrations-Tracker bestand 16 von 16 Tests.
  • Das dauerhafte Ergebnis ist eine Migrationsplattform: von Features besessene Routen, gemeinsame Navigation, eine wiederverwendbare Seitenstruktur, BFF-Grenzen, konsistente State-Muster und Release-Absicherungen, die die React-Arbeit wachsen liessen, während das Legacy-Produkt verfügbar blieb.

Zahlen, mit ihren Grenzen

  • Echte React-Routen auf HEADVorher10Nachher112Juni-Baseline → September-Snapshot 2026
  • Echte Routen auf der Staging-Linie108ohne 42 Platzhalter und 2 reine Entwicklungsrouten
  • Staging-Routen mit Abbildung auf eine Legacy-URL65 von 10816 native React-Routen, 27 Detail-/Erstell-/Bearbeitungspfade oder ohne Abbildung
  • JavaScript-EinstiegsartefaktVorher1'764'861 BytesNachher63'249 Bytesnur der Einstiegs-Chunk, nicht das gesamte JavaScript
  • Frontend-Testdateien · Migrations-Tracker317 Dateien · 16 / 16 Testshistorische Zählung, September 2026

Was diese Zahlen nicht sagen

  • Eine Route ist kein vollständiges fachliches Feature, und Routenzahlen sagen nichts darüber, wie viel des Legacy-Produkts migriert ist.
  • Die Verkleinerung des Einstiegs-Chunks ist keine Verkleinerung des insgesamt übertragenen JavaScripts und keine Verkürzung der Ladezeit; beides wurde nicht gemessen.
  • Der Betrieb im Staging ist eine technische Beobachtung, kein Smoke-Test Route für Route, keine Kundenaktivierung und keine Produktionsnutzung. Diese Daten wurden nicht als überprüfbare Kennzahlen aufbewahrt, deshalb stelle ich die Arbeit nicht als abgeschlossene Migration dar.

Was es zeigt

Ich kann einen Migrationspfad entwerfen, der einen neuen Stack in einem produktiven Legacy-Produkt wachsen lässt — mit Sicherheitsgrenzen, wiederholbaren Mustern und Release-Absicherungen — und bis zur Stabilisierung verantwortlich bleiben.

Zuordnung

Ich habe Frontend-Architektur und Umsetzungsrichtung verantwortet und dabei KI-Unterstützung für Repository-Analyse und Umsetzung genutzt, während Entscheidungen, Review und Release-Bereitschaft bei mir blieben. Die auf der Plattform gebauten fachlichen Features gehören dem ganzen Team, und die Wahl der UI-Bibliothek war eine gemeinsame Entscheidung.