React Ordnerstruktur die skaliert

#React Ordnerstruktur
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

React Ordnerstruktur die skaliert

Eine Ordnerstruktur ist nicht „nur Ästhetik". Sie kodiert Grenzen, Eigentümerschaft und Änderungssicherheit in Ihre Codebasis. Wenn Ihr Team mehr als ein paar Features pro Monat ausliefert, zeigt sich der Unterschied zwischen „funktioniert" und „skaliert" schnell: längere Pull Requests, mysteriöse Regressionen, zirkuläre Importe und eine wachsende Scheu davor, „gemeinsamen" Code anzufassen.

Dieser Leitfaden gibt Ihnen eine produktionsreife React-Ordnerstruktur, die skaliert, sowie die Regeln, die sie zum Funktionieren bringen. Er richtet sich an Engineers und Tech Leads, die React-Code wartbar halten möchten, während die App und das Team wachsen.

Was „skalierbar" für eine React-Codebasis bedeutet

Eine skalierbare Struktur ist eine, die sich noch vorhersehbar anfühlt, nachdem:

  • Sie 20+ Screens und mehrere User Journeys hinzugefügt haben
  • Sie Berechtigungen, Feature Flags und mehrere Layouts eingeführt haben
  • Sie neue Entwickler ohne Stammwissen einarbeiten
  • Sie die Arbeit auf Teams aufteilen (oder zumindest auf klar definierte Verantwortungsbereiche)

Das Ziel ist nicht, das eine wahre Layout zu finden. Das Ziel ist, Änderungen günstig zu machen.

Die 3 Ordnerstrukturen, die Teams typischerweise ausprobieren (und warum eine gewinnt)

Die meisten React-Repos beginnen mit einer dieser Organisationsachsen:

StrategieWie sie aussiehtWofür sie optimiertWo sie zuerst bricht
Layer-first/components, /hooks, /utils, /servicesEinfacher Start, leichtes Auffinden „einer Komponente"„Shared" wird zur Sammelstelle, Features werden verstreut
Route-first/routes/orders, /routes/settingsFunktioniert gut mit verschachtelten RouternCross-Route-Wiederverwendung wird unklar, Domain-Logik leckt
Feature-first (empfohlen)/features/billing, /features/invite-teamKlare Eigentümerschaft, lokales Denken, paralleles ArbeitenErfordert explizite Abhängigkeitsregeln, um unordentliches Teilen zu verhindern

Für die meisten Produkt-Teams skaliert feature-first am besten, weil es der Art entspricht, wie Sie liefern: Sie liefern Features, keine „Komponenten". Sie können immer noch geteilte UI und geteilte Infrastruktur haben, aber diese müssen absichtlich eingeschränkt sein.

Eine Ordnerstruktur-Vorlage, die skaliert

Hier ist eine pragmatische Struktur, die für React-SPAs (Vite + React Router) funktioniert und für Meta-Frameworks (Next.js, Remix) angepasst werden kann. Die Namen sind weniger wichtig als die Trennung.

Ein sauberer Projekt-Ordnerbaum für eine React-Anwendung mit Top-Level-Verzeichnissen wie app, routes, features, shared, entities und testing, mit kurzen Anmerkungen dazu, was wohin gehört.

Empfohlenes Top-Level-Layout

src/
  app/
    providers/
    layout/
    config/
    bootstrap.tsx

  routes/
    _shell/
    home/
    settings/

  features/
    invite-team/
      api/
      model/
      ui/
      index.ts
    billing/
      api/
      model/
      ui/
      index.ts

  entities/
    user/
      model/
      api/
      index.ts
    organization/
      model/
      api/
      index.ts

  shared/
    ui/
    lib/
    api/
    config/

  test/
    factories/
    msw/

  types/

Diese Struktur setzt eine einfache Idee durch:

  • routes/ orchestriert: Seitenzusammenstellung, Loader, Route-Level-Error-Boundaries, Zugriffskontrollen
  • features/ liefert Arbeit: benutzerorientierte Fähigkeiten (Invite Team, Billing, Export CSV)
  • entities/ enthält Domain-Bausteine: User, Organization, Invoice (Typen, Invarianten, Domain-Helfer)
  • shared/ ist langweilige Infrastruktur: generische UI-Primitiven und Bibliotheken ohne Geschäftsbedeutung

Wenn Sie bereits ein Next.js App Router-Repo haben, entspricht das Äquivalent darin, Ihre app/-Routen als Orchestrierungsschicht zu behalten, während features/, entities/ und shared/ framework-agnostisch bleiben.

Was wohin gehört (mit durchsetzbaren Regeln)

Eine skalierbare Struktur braucht Regeln, die Menschen unter Druck wiederholen können. Der einfachste Weg, dies umsetzbar zu machen, besteht darin, „erlaubte Abhängigkeiten" zu definieren.

Abhängigkeitsregeln (der Teil, den die meisten Teams überspringen)

OrdnerDarf importieren vonSollte nicht importieren vonWarum
shared/Nur shared/features/, routes/, entities/Hält die Shared-Schicht wirklich wiederverwendbar
entities/shared/, andere entities/ (vorsichtig)features/, routes/Verhindert, dass Domain-Konzepte von UI-Workflows abhängen
features/shared/, entities/Andere features/ (standardmäßig)Vermeidet „Feature-Spaghetti" und versteckte Kopplung
routes/features/, entities/, shared/Nichts (es ist die Spitze)Routen komponieren die App, sie dürfen mehr wissen

Standardregel: Features sollten nicht von anderen Features importieren.

Wenn Sie den Drang dazu spüren, haben Sie normalerweise eines davon entdeckt:

  • Ein fehlendes Domain-Konzept (zu entities/ befördern)
  • Eine wirklich wiederverwendbare UI-Primitive (zu shared/ui/ befördern)
  • Einen Workflow, der eigentlich ein Produktbereich ist (Features zusammenführen oder eine klarere Grenze definieren)

Was innerhalb eines feature/ lebt

Halten Sie Feature-Interna langweilig und konsistent. Diese Vorlage funktioniert gut:

features/invite-team/
  api/
    inviteTeam.ts
    useInviteTeamMutation.ts
  model/
    schemas.ts
    types.ts
  ui/
    InviteTeamDialog.tsx
    InviteTeamForm.tsx
  index.ts

Richtlinien:

  • api/ enthält Netzwerk- und Server-State-Integration (Fetcher, Query Hooks, Mutation Hooks).
  • model/ enthält Validierung, Typen und Geschäftsregeln für das Feature (oft Zod-Schemas plus abgeleitete Typen).
  • ui/ enthält Komponenten, die im Feature-Kontext bedeutungsvoll sind.
  • index.ts ist die einzige öffentliche Oberfläche (exportieren Sie die minimalen Dinge, die die Route benötigt).

Dadurch wird jedes Feature zu einem kleinen Modul, über das Sie nachdenken, es testen und refaktorieren können, ohne das gesamte Repo zu scannen.

Was in entities/ gehört

Entities sind Ihre Domain-Nomen. Sie sind keine „Modelle" im MVC-Sinne, sondern stabile Konzepte, auf die mehrere Features verweisen könnten.

Gut passende Beispiele:

  • entities/user/model/permissions.ts
  • entities/invoice/model/money.ts
  • entities/organization/api/getOrganization.ts

Gegenbeispiele (in features/ belassen):

  • „InviteTeamDialog-Zustandsmaschine" (Feature-Workflow)
  • „Billing-Seiten-Filter und URL-State" (Route + Feature-Orchestrierung)

Dateibenennungs- und Export-Konventionen, die Churning verhindern

Eine Ordnerstruktur hilft, aber Skalierungsfehler entstehen oft durch inkonsistente Exporte und Benennung. Wählen Sie Standardeinstellungen, die Refaktorierungen günstig machen.

Ein praktisches Konventions-Set

AnliegenEmpfehlungWarum es skaliert
KomponentendateienPascalCase.tsx für React-KomponentenMacht UI in Diffs leicht erkennbar
Nicht-Komponenten-ModulecamelCase.tsTrennt Verhaltensmodule von UI
Öffentliche ExporteNur über index.ts pro ModulMacht Modulgrenzen explizit
ImporteAbsolute Aliase bevorzugen (nicht ../../..)Verbessert Refaktorierungen und Lesbarkeit

Ein Hinweis zu „Barrel Files":

  • Verwenden Sie index.ts, um die öffentliche API eines Features oder einer Entity zu definieren.
  • Vermeiden Sie tiefe Barrel-Muster, die die Hälfte des Repos re-exportieren. Sie verschleiern Abhängigkeiten und können Builds verlangsamen.

Routing als Orchestrierungsnaht (React Router und Next.js)

Ein häufiger Fehler ist es, Routen mit Domain-Logik, Daten-Shaping und UI-Details zu füllen. Ein skalierbarer Ansatz ist das Gegenteil: Routen komponieren.

React Router Beispiel

  • routes/settings/route.tsx komponiert:
    • Zugriffskontrollen
    • Datenladen
    • Layout
    • Feature-Komponenten

Es sollte nicht enthalten:

  • Low-Level-Fetch-Aufrufe
  • Schema-Validierungslogik
  • Komplexe Transformationslogik, die isoliert getestet werden sollte

Next.js App Router Anpassung

In Next.js ist die Routennaht Ihr app/.../page.tsx (und manchmal layout.tsx). Das gleiche Prinzip gilt:

  • Halten Sie page.tsx als Kompositionsschicht.
  • Stecken Sie Feature-Logik in features/... und importieren Sie sie.
  • Halten Sie geteilte Utilities in shared/.

Dies verhindert die langsame Drift, bei der Ihr app/-Verzeichnis zu Ihrer gesamten Anwendung wird.

Gemeinsame UI: der schnellste Weg, eine „Rumpelkammer" zu schaffen

Jedes Team erstellt letztendlich components/ und beginnt, alles dort hinzuzufügen.

Eine skalierbare Shared-UI-Schicht ist absichtlich begrenzt:

  • shared/ui/ enthält Primitiven ohne Geschäftsbedeutung (Button, Modal, Tabs).
  • Feature-Komponenten bleiben in features/<name>/ui/, auch wenn sie einmal oder zweimal wiederverwendet werden.

Ein guter Test ist die Benennung:

  • Wenn es nach einem Geschäftskonzept benannt ist (InvoiceStatusBadge), ist es keine Shared UI.
  • Wenn es eine Primitive ist (Badge), kann es sein.

Dies verhindert die „Shared-Component-Steuer", bei der die Änderung einer UI-Komponente unerwartet fünf nicht verwandte Screens bricht.

Datenzugriff: Struktur als Sicherheitsmechanismus

Skalierende React-Apps bedeuten normalerweise skalierbaren Server State. Unabhängig davon, ob Sie TanStack Query, RTK Query, SWR oder Framework-Loader verwenden, ist die Organisation wichtig.

Praktische Standardeinstellungen:

  • Stecken Sie feature-spezifische Query Hooks in das Feature (features/billing/api/useInvoicesQuery.ts).
  • Stecken Sie entity-level Fetcher und Typen in Entities (entities/user/api/getUser.ts).
  • Stecken Sie den Low-Level-HTTP-Client in Shared (shared/api/httpClient.ts).

Dieses Setup macht die Abhängigkeitsrichtung offensichtlich: Shared Client, Entity-Fetcher, Feature-Hooks, Route-Komposition.

Tooling, das die Struktur durchsetzt (damit sie die Realität überlebt)

Eine Ordnerstruktur, die von Disziplin abhängt, wird unter Deadlines versagen. Setzen Sie Regeln in CI durch.

Wertvolle Leitplanken:

  • TypeScript-Pfadaliase (für lesbare Importe und einfachere Verschiebungen)
  • ESLint no-restricted-imports, um features/* vom Import anderer features/* zu blockieren
  • ESLint-Importregeln, um tiefe Importe wie features/x/ui/internal/Thing zu verhindern
  • Eine „Nur öffentliche API"-Konvention (Import aus features/invite-team, nicht aus features/invite-team/ui/...)

Diese sind nicht teuer hinzuzufügen und zahlen sich das erste Mal aus, wenn sie eine versehentliche zirkuläre Abhängigkeit stoppen.

Ein Migrationsplan für ein bestehendes unordentliches React-Repo

Sie brauchen kein Rewrite. Ein Folder-Reset kann inkrementell sein.

Eine sichere Sequenz:

  • Beginnen Sie damit, shared/ einzuführen und echte Primitiven dorthin zu verschieben (Button, Modal, formatDate).
  • Wählen Sie einen stark veränderten Bereich und verwandeln Sie ihn in ein Feature-Modul (features/invite-team). Fügen Sie eine index.ts hinzu und importieren Sie nur darüber.
  • Fügen Sie eine ESLint-Einschränkung hinzu, die neue Cross-Feature-Importe verhindert, auch wenn Sie noch einige Legacy-Importe haben.
  • Befördern Sie stabile Nomen nur dann in entities/, wenn zwei oder mehr Features sie tatsächlich benötigen.
  • Halten Sie Routen als Orchestrierung. Jedes Mal, wenn Sie eine Route berühren, schieben Sie Logik in Features/Entities hinunter.

Wenn Sie dies über einige Sprints hinweg tun, „gewinnt" die Struktur durch Schwerkraft und das alte Layout hört auf zu wachsen.

Warum das auch außerhalb von Software-Teams wichtig ist

Selbst in Nicht-Software-Branchen wird wartbarer UI-Code zu einem operativen Vorteil. Denken Sie an Unternehmen, die physische Infrastruktur verkaufen und installieren, wie ein Anbieter von Photovoltaikanlagen, Notstrom und Elektroinstallationen. Sie brauchen oft interne Portale für Angebotserstellung, Terminplanung, Wartung und Kundenkommunikation. Wenn Sie diese Art von React-App aufbauen, macht eine Feature-First-Struktur es einfacher, von „einem Dashboard" zu einem Multi-Workflow-System zu wachsen. Als reales Beispiel eines solchen Geschäftskontexts siehe Notstrom & Elektrotechnik Sven Sanny.

Wann Sie externe Hilfe hinzuziehen sollten

Wenn Sie den Schmerz bereits spüren (langsames Onboarding, fragiler Shared Code, Pull Requests, die 30 Dateien über nicht verwandte Bereiche hinweg berühren), ist die schnellste Lösung normalerweise ein kurzer Architektur- und Codebase-Review mit Fokus auf:

  • Modulgrenzen und Abhängigkeitsrichtung
  • Refaktorierungsplan mit risikoarmen Bruchstellen
  • Ein minimales Set von durchgesetzten Regeln (Lint, CI-Gates)

Wolf-Tech bietet Full-Stack-Entwicklung und Code-Quality-Beratung für Teams, die bestehende React-Codebasen modernisieren oder skalieren möchten. Wenn Sie ein zweites Paar erfahrener Augen auf Ihre Struktur haben möchten, bevor sie zu Legacy wird, können Sie Optionen auf wolf-tech.io erkunden.