Der weiße Cursor in VS Code blinkte lautlos, aber es wurden keine Typhinweise angezeigt. Der frustrierte Seufzer meines Kollegen hallte durch unseren Slack-Anruf – seine ältere Maschine hatte TypeScript-Vorschläge schließlich vollständig aufgegeben. Nachdem wir ein Jahr lang an der Entwicklung unserer Next.js-Anwendung gearbeitet hatten, stießen wir auf eine Wand, vor der ich mich gefürchtet hatte: Unsere monolithische Codebasis war zu groß geworden, um noch komfortabel zu sein.
Als ich dieses Projekt zum ersten Mal startete, schien Next.js die perfekte Wahl zu sein. Aufgrund meiner Erfahrung mit einfachen React SPAs mit React Router und Express – und sogar früheren Erfahrungen mit PHP – fühlte sich die Idee, Server- und Client-Code gemeinsam zu platzieren, intuitiv an. Der herkömmlichen Meinung folgend haben wir unseren Code nach Funktionalität und nicht nach technischen Gesichtspunkten organisiert. Authentifizierung, Interessenten, Konten, Teamfunktionen – jedes befand sich in einem eigenen Modul, komplett mit seinen eigenen Typen, Dienstprogrammen, Konstanten und serverseitigem Code.
„Zuerst war es wunderschön“, erinnere ich mich, wie ich in diesen ersten Monaten dachte. Durch die Arbeit am Kontenmodul war alles, was Sie brauchten, direkt da – Komponenten, Hooks, tRPC-Funktionen, sogar Prisma-Dateien – alles in einem einzigen Ordner. Es war die Entwicklererfahrung, die ich mir immer gewünscht hatte.
Sieben Monate später traten die ersten Warnzeichen auf. Der Sprachserver von TypeScript geriet in Schwierigkeiten, Vorschläge kamen immer langsamer und die Erstellungszeiten stiegen langsam an. Während meine leistungsstarke Entwicklungsmaschine noch damit zurechtkam, gab die ältere Hardware meines Kollegen der Komplexität völlig nach.
Wir standen vor einem klassischen technischen Scheideweg: Geld in die Lösung des Problems stecken oder Ingenieurstunden investieren, um es richtig zu lösen. Natürlich könnten wir unsere Hardware aufrüsten – die TypeScript-Leistung wirkt sich nur auf die Entwicklung aus, nicht auf die Produktion. Aber irgendetwas an dieser Lösung fühlte sich wie ein Pflaster an. Wir haben den schwierigeren Weg gewählt: die Umgestaltung unseres Monolithen in ein Monorepo mithilfe von Turborepo.
Der erste Schritt war überraschend einfach: Migrieren Sie die Struktur, ohne Code aufzuteilen. Ich habe einen Apps-Ordner erstellt, der unsere Web-App enthält, und zwei Standard-Turborepo-Pakete für die ESLint- und TypeScript-Konfiguration hinzugefügt. Aber der eigentliche Test würde darin bestehen, unsere Kernfunktionalität zu verschieben und gleichzeitig die Typinferenz beizubehalten.
Ich habe mit unserer Datenbankschicht begonnen und den gesamten Prisma-bezogenen Code in ein eigenes Paket verschoben. Nach einigen Optimierungen des package.json-Exports hielt ich den Atem an und überprüfte die Typen in unserer Haupt-App. Sie haben perfekt funktioniert. Noch besser: Als mein Kollege die Änderungen vornahm, erhielt er zum ersten Mal seit Wochen wieder IntelliSense-Vorschläge. Wir waren auf der Spur.
Als nächstes kam tRPC, was logisch erschien – ein weiteres eigenständiges Stück serverseitiger Funktionalität. Aber hier wurde es interessant. Was als „einfaches Verschieben von tRPC“ begann, führte zu einer Reihe unerwarteter Abhängigkeiten:
Diese Migration hat mir einige wichtige Lektionen über Architektur und Entwicklungspraktiken beigebracht:
Server-Client-Trennung ist wichtig: Während Next.js es einfach macht, Server- und Client-Code zu mischen, kann diese Bequemlichkeit zu chaotischen Architekturen führen. Ich habe Typen und Konstanten über Grenzen hinweg importiert, ohne über die Auswirkungen nachzudenken.
Mit Monorepo beginnen: Wenn ich heute noch einmal anfangen würde, würde ich vom ersten Tag an mit Turborepo beginnen. Es fügt minimale Komplexität hinzu und zwingt Sie gleichzeitig, auf gesunde Weise über Abhängigkeiten und Architektur nachzudenken.
Explizite Abhängigkeiten sind besser: Das Aufbrechen des Monolithen zwang uns, unsere Abhängigkeitsbeziehungen zu visualisieren und zu hinterfragen. Sind diese Verbindungen notwendig? Haben wir zirkuläre Abhängigkeiten geschaffen? Diese Einschränkungen drängten uns zu besseren architektonischen Entscheidungen.
Die Migration ist noch nicht abgeschlossen. Unser Servercode und unsere gemeinsamen Dienstprogramme müssen noch ordnungsgemäß organisiert werden, und wir überdenken unsere Modulstruktur, da tRPC- und Datenbankebenen nun getrennt voneinander existieren. Aber unsere Entwicklungserfahrung hat sich bereits dramatisch verbessert.
Für alle, die eine Next.js-Anwendung erstellen, die sich skalieren lässt, sollten Sie in Betracht ziehen, mit einer Monorepo-Struktur zu beginnen. Die Anfangsinvestition ist minimal, aber die architektonischen Leitplanken, die sie bietet, sind von unschätzbarem Wert. Ihr zukünftiges Ich – und die älteren Laptops Ihres Teams – werden es Ihnen danken.
Das obige ist der detaillierte Inhalt vonVom Monolith zum Monorepo: Eine Next.js-Migrationsgeschichte. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!