Auswahl einer React-Verzeichnisstruktur: Sieben Strukturen und ihre Grenzen
Vergleichen Sie flache, typbasierte, featurebasierte, Atomic Design-, DDD-, Feature-Sliced-Design- sowie monorepo-basierte Strukturen für React und erfahren Sie, ab welcher Größenordnung jede dieser Ansätze nicht mehr funktioniert.
Quellen: React Folder Structure: 7 Ways to Organize a React App (And Exactly When Each One Breaks)
Jede wachsende React-Codebasis stößt irgendwann auf dieselbe Frage: Wohin soll diese Datei? Es gibt keine offizielle Antwort, doch Teams verbringen ganze Sprints damit, zwischen verschiedenen Strukturen zu wechseln, und Interviewer befragen Kandidaten weiterhin dazu. Diese Anleitung vergleicht sieben gängige Wege, um eine React-Anwendung zu organisieren – von einer einfachen src/-Ordnerstruktur bis hin zu einem Multi-App-Monorepo – unter Verwendung einer Referenzanwendung. Für jede Struktur erfahren Sie, um was es sich handelt, wer sie tatsächlich verwendet, wo ihre Stärken liegen, wo sie Schwächen aufweisen und an welchem Punkt sie nicht mehr funktioniert, damit Sie die kostengünstigste Struktur wählen können, die zu Ihrer Anwendung passt.
Keine offizielle Stelle wird das für Sie auswählen
Die alte Dokumentation von React enthielt einst eine Seite zur Dateistruktur. Dort hieß es zunächst, React habe keine Meinung dazu, wie Dateien in Ordnern platziert werden sollen, und es wurde empfohlen, dass Personen, die ein Projekt starten, nicht länger als fünf Minuten damit verbringen sollten, eine Struktur auszuwählen. Die Seite wurde später entfernt, als die Dokumentation auf react.dev verlegt wurde, und es wurde nichts an ihre Stelle gesetzt. Heute sagt React im Grunde gar nichts über Ordnner aus. Die alten Richtlinien sind weiterhin auf der archivierten FAQ-Seite zu finden.
Auch die Frameworks und Tools rund um React sind genauso vage in ihren Angaben. Next.js bezeichnet sich selbst als unentschlossen, was die Organisation und Platzierung der Projektdateien angeht. Das React-Template von Vite erzeugt nur wenige Quelldateien und keinerlei Ordnner. Keine Instanz im Ökosystem gibt Anweisungen, was zu tun ist.
Dieses Fehlen erklärt vieles. Strukturen wie Bulletproof React haben vor allem aufgrund des Vorschlags eines Ordnerlayouts Zehntausende GitHub-Sterne gesammelt, und Entwickler diskutieren ständig über dieses Thema. Die ehrliche Einordnung für alles, was folgt, ist, dass es sich bei diesen sieben Strukturen um Konventionen und nicht um Standards handelt. Keine davon wird von React selbst befürwortet. Entscheidend ist es, die Kosten jeder Struktur zu verstehen und vor allem herauszufinden, wo sie versagen – denn das ist der entscheidende Faktor, sobald eine Anwendung real umgesetzt wird.
Die beiden Prinzipien hinter jeder Struktur
Die Erfinder sind entspannter als die Verfechter
Die Menschen, die diese Methodologien entwickelt haben, neigen dazu, sie weitaus lockerer zu betrachten als diejenigen, die sie fördern. Brad Frost, der Atomic Design erfunden hat, veröffentlichte 2019 „Extending Atomic Design“. Darin beschreibt er, wie ihn Menschen kontaktierten, um seine Terminologie zu „korrigieren“, betont, dass Atomic Design kein starres Dogma sei, und sagt, Teams, die neun Phasen nach Mitgliedern der Band Chicago benennen möchten, sollten es gerne tun. Sie können den Artikel unter Extending Atomic Design lesen. Der Schöpfer der Methode sagt Ihnen, dass die Namen keine Bedeutung haben, während irgendwo eine Code-Review wegen der Frage stecken bleibt, ob ein Komponente als Molekül gilt oder nicht.
Die alten Dokumente von React erwähnten Atomic Design ebenfalls ausdrücklich. Darin wurde darauf hingewiesen, dass einige Entwickler Komponenten je nach ihrer Rolle in der Anwendung in Ordner aufteilen, wobei Atomic Design als Beispiel für diesen Ansatz herangezogen wurde. Zudem wurde empfohlen, solche Methoden eher als nützliche Beispiele denn als strenge Regeln zu betrachten. Da dies aus den eigenen Dokumenten von React stammt, handelt es sich um die am besten belegte Kritik an einem strengen Anwenden von Atomic Design in React.
Screaming Architecture
In dieser Debatte gibt es tatsächlich nur einen bedeutenden Argument, und das ist älter als React. Im September 2011 veröffentlichte Robert C. Martin „Screaming Architecture“. Seine Frage lautete: Wenn jemand sich die obersten Verzeichnisse Ihrer Anwendung ansieht, erkennt man dort sofort ein Gesundheitssystem, ein Buchhaltungssystem oder ein Lagerverwaltungssystem? Sein Fazit war, dass eine Architektur den Lesern etwas über das System selbst vermitteln sollte – und nicht über die Frameworks, die zufällig verwendet werden.
Diese Idee liegt der Struktur jedes featuresbasierten Layouts zugrunde. Ein src/-Verzeichnis, das components/, hooks/, utils/ und services/ enthält, signalisiert, dass es sich um eine React-Anwendung handelt. Ein src/-Verzeichnis, das checkout/, billing/ und inventory/ enthält, gibt an, womit sich das Unternehmen beschäftigt. Jede Struktur in diesem Leitfaden ist entweder eine Variante dieser Idee oder eine bewusste Ablehnung derselben.
Kolokation
Das zweite Prinzip hat im Alltag den größten Einfluss. Kent C. Dodds bezeichnet es als Kolokation: Halten Sie den Code so nah wie möglich am Ort, an dem er relevant ist. Er schreibt Dan Abramov eine präzisere Formulierung zu, nämlich dass Dinge, die zusammen ändern, so nah beieinander platziert werden sollten, wie es vernünftig ist. Die ausführliche Erklärung finden Sie in Colocation.
Aus dieser Perspektive heraus beantwortet jede Struktur in diesem Leitfaden eine einzige Frage: Welche Teile Ihres Codes entwickeln sich als Einheit? Wenn Sie diese Frage richtig beantworten, entstehen die Ordnernamen fast von selbst. Wenn Sie sie falsch beantworten, wird Ihnen keine Methode helfen.
Eine Anmerkung zu einem bekannten Zitat: Der Ratschlag, „Dateien so lange verschieben, bis es richtig wirkt“, wird häufig Dan Abramov zugeschrieben und manchmal als offizielle React-Dokumentation angeführt. Es handelt sich dabei jedoch nicht um Dokumentation; die offiziellen Dokumente enthalten stattdessen den fünfminütigen Ratschlag, und Abramovs Aussage stammt von einem Tweet, der anscheinend gelöscht wurde. Die Idee dahinter ist sinnvoll; betrachten Sie die Zuschreibung als Volksmärchen.
Eine Referenz-App, sieben Anordnungen
Der Vergleich von Layouts ist nur sinnvoll, wenn die Anwendung unverändert bleibt; andernfalls vergleicht man eher Beispiele als Strukturen. Daher organisiert jedes der untenstehenden Layouts dieselbe kleine E-Commerce-Anwendung mit denselben Dateien:
- Funktionen: Authentifizierung (Anmelden und Registrierung), Katalog (Produktiliste und -details), Warenkorb, Bestellabwicklung sowie Bestellungen.
- UI-Primitiven:
Button,Input,Modal,BadgeundSpinner. - Hooks:
useAuth,useCart,useProducts,useCheckoutunduseDebounce. - API-Module:
authApi,productsApi,checkoutApi,ordersApisowie ein gemeinsamerapiClient. - Weitere Elemente: eine
cartStoresowie ein HilfsfunktionformatPrice.
Vier dieser Dateien sind absichtlich problematisch, weil sie zeigen, wo bei jedem Layout Probleme auftreten:
Modal.jsxwird je nach Person, die sie kategorisiert, als Atom, Molekül oder Organismus bezeichnet, was die Taxonomie von Atomic Design betont.useDebounce.jsist völlig allgemein, wird jedoch nur bei der Katalogsuche verwendet. Handelt es sich um einen globalen Hook oder um einen Katalog-Hook?CartTotal.jsxwird sowohl im Warenkorb-Panel als auch auf dem Zahlungsbildschirm dargestellt, wodurch sein Verantwortlicher unklar ist.formatPrice.jswirkt wie eine einfache Hilfsfunktion – es sei denn, Geld ist ein echtes Domänenkonzept; in diesem Fall sollte es sich um einMoney-Wertobjekt handeln, und seine Platzierung in einer Hilfsfunktionen-Datei ist ein Fehler.
Halten Sie diese vier Punkte im Hinterkopf. Jede Struktur handhabt sie unterschiedlich, und genau diese Unterschiede unterscheiden ein Layout vom anderen.
Einfache Struktur
Was das ist
Alles befindet sich direkt in src/, es gibt kaum oder gar keine Verzeichnisse, und die Dateien sind alphabetisch geordnet, da nichts anderes eine Ordnung vorschreibt.
Niemand hat dies als Methodik veröffentlicht. Es ist einfach der Ausgangspunkt für jedes Projekt – und überraschenderweise bleiben viele Projekte auch dort. Sogar Reacts Dokumentation zur Vergangenheit empfahl dies als Ausgangspunkt: Wenn Sie sich festgefahren fühlen, legen Sie alle Dateien in ein Verzeichnis, und sobald es groß genug ist, um aufgeteilt zu werden, wissen Sie bereits, welche Dateien Sie tendenziell zusammen bearbeiten. Das ist wirklich guter Rat. Eine aus beobachteten Änderungsmustern abgeleitete Struktur ist besser als eine, die bereits am ersten Tag aufgezwungen wird.
Wer verwendet sie
Prototypen, Experimente, Sandbox-Demos, Hackathon-Projekte, Hausaufgaben sowie interne Tools mit nur einem Betreuer. Zudem werden leise viele Produktionsanwendungen beschrieben, die nie refaktorisiert wurden.
Das offizielle Vite React-Template ist im Grunde einfach aufgebaut. Durch Ausführung von npm create vite@latest -- --template react-ts entstehen main.tsx, App.tsx, App.css, index.css sowie ein assets/-Verzeichnis – es gibt weder ein components/- noch ein features/-Verzeichnis. Da das Scaffold keine Präferenzen äußert, verlagert sich die Debatte einfach auf jedes Team, das es verwendet.
Vorteile
- Die Navigation kostet nichts. Es gibt nur ein Verzeichnis, und der Dateiauswahler findet alle Dateien anhand ihres Namens. Da es keine Entscheidungen bezüglich der Platzierung gibt, gibt es auch keine Streitigkeiten darüber.
Schwächen
- Die Skalierbarkeit endet plötzlich bei etwa 30 Dateien. Der Zusammenbruch erfolgt nicht schrittweise: Eines Tages passt die Dateiliste nicht mehr in die Seitenleiste und die Form der Anwendung verschwindet.
- Die alphabetische Reihenfolge hat keine Bedeutung.
authApi.jssteht nebenApp.jsxallein deshalb, weil beide mit A beginnen, währenduseAuth.jsundLoginForm.jsx, die man stets zusammen bearbeitet, weit auseinanderliegen. Die Sortierreihenfolge wirkt somit direkt gegen eine logische Anordnung. - Es gibt keine Grenzen, die durchgesetzt werden könnten. Jedes Datei kann jede andere importieren, zyklische Abhängigkeiten entstehen unbemerkt, und wenn man schließlich Grenzen setzen will, ist jede Verletzung bereits von Bedeutung.
- Das Entfernen einer Funktion wird zu einer Art Detektivarbeit. Um das Checkout-Verfahren zu löschen, muss man die Dateien durchlesen, um herauszufinden, welche dazu gehören – schließlich gibt es kein Verzeichnis, das man löschen könnte.
Wo es versagt
Ab etwa 30 Dateien oder sobald ein zweiter Entwickler hinzukommt – wobei dieser zweite Schwellenwert noch wichtiger ist. Eine flache Struktur funktioniert, weil eine Person sich das Ganze im Kopf vorstellen kann. Bei zwei Personen gibt es jedoch zwei „Karten“, die sofort auseinanderdriften.
typbasierte Struktur
Was das ist
Die Dateien werden nach ihrer Art gruppiert: Komponenten zusammen, Hooks zusammen, API-Aufrufe zusammen, Stores zusammen. In den älteren Dokumentationen zu React wurde dies als Gruppierung nach Dateityp beschrieben und als eine von zwei gängigen Vorgehensweisen dargestellt, wobei keine bevorzugt wurde. Für die Referenzanwendung bedeutet das Button, Modal, CartItem, CheckoutForm sowie alle anderen Komponenten in components/, alle Hooks in hooks/, alle API-Module in api/ und cartStore in store/.
Weil die meisten Tutorials diese Struktur vermitteln, ist sie auch die häufigste in echten Projekten.
Wer verwendet sie
Fast jedes React-Tutorial, die meisten Bootcamp-Projekte sowie ein großer Teil der Produktionsanwendungen wuchsen ohne Plan aus einem flachen Layout hervor. Es ist der einfachste Schritt: Sobald src/ unübersichtlich wird, ist ein Ordner components/ die natürliche erste Reaktion.
Vorteile
- Dafür ist keine Erklärung nötig. Niemandem musste jemals erklärt werden, was in
hooks/gehören soll, sodass neue Entwickler bereits am ersten Tag mit einem Wortschatz beitragen können, den sie bereits kennen. - Die Klassifizierung ist trivial. Ein Komponente landet in
components/, ein Hook inhooks/, und die Datei selbst gibt an, um was es sich handelt. Im Vergleich dazu kann man bei Atomic Design stundenlang über eine einzelne Komponente diskutieren. - Er ist für kleine Skalen völlig ausreichend. Bei etwa weniger als 50 Dateien funktioniert er gut, und ein Umstieg würde mehr Kosten verursachen, als er bringt.
Schwächen
- Es wird das Framework statt des Produkts angekündigt, was genau dem Widerspruch von Screaming Architecture entspricht. Obergrenzkataloge mit den Namen
components/,hooks/,api/undutils/deuten einem Neuling darauf hin, dass React verwendet wird, ohne etwas über die Funktionen der Anwendung zu verraten. Ein Online-Shop und ein Krankenhausscheduler sehen von der Wurzel aus identisch aus.
CartItem.jsx in components/, useCart.js in hooks/, cartApi.js in api/ sowie cartStore.js in store/ geändert werden müssen. Das sind vier Verzeichnisse und vier Scrollpositionen für eine einzige logische Änderung. Die Zusammenlegung von Dateien ist gegen die Richtlinien: Die Dateien, die am ehesten im selben Commit geändert werden, werden so weit wie möglich voneinander entfernt platziert.components/ wird zu einem neuen, flachen Verzeichnis. In der Referenzanwendung enthält es etwa 18 Dateien ohne interne Gliederung. Das Problem der flachen Struktur wird bei Hooks und API-Modulen gelöst, taucht aber erneut im Verzeichnis auf, das den größten Teil des Codes enthält.ProductCard daran, useCheckout zu importieren; die Ordner dienen rein dekorativen Zwecken.Wo es versagt
Ab etwa 100 Dateien, oder wenn components/ mehr als 20 Einträge enthält. Das Warnsignal zeigt sich im Verhalten: Entwickler fügen Dateinamen Präfixe wie CartItem, CartDrawer und CartTotal hinzu, um die Gruppierung nachzubilden, die die Ordner nicht bieten. Diese Präfixe sind ein Zeichen dafür, dass der Codebasis Funktionen in eigenen Ordnern fehlen.
strukturierter Aufbau nach Funktionen
Was das ist
Die Dateien werden nach ihrer Funktion gruppiert. Jede Funktionalität enthält ihre Komponenten, Hooks, API-Aufrufe und Zustände an einem Ort; gemeinsam genutzte Primitiven befinden sich in einem gemeinsamen Bereich, und Funktionalitäten werden auf Anwendungsebene zusammengesetzt, ohne dass sie einander importieren müssen.
Reacts ältere Dokumentation bezeichnete diese Gruppierung als Funktionalitäten oder Routen und bot eine praktische Methode, um die Grenzen zu bestimmen. Da die Bedeutung von „Funktionalität“ nicht universell ist, wählen Sie selbst die Granularität aus. Wenn Ihnen keine Liste der obersten Verzeichnisse einfallen, können Ihre Nutzer die wichtigsten Teile des Produkts beschreiben und ihr eigenes mentales Modell als Leitfaden verwenden. Das ist wohl der nützlichste praktische Ratschlag zu diesem Thema: Ihre Nutzer wissen bereits, wo die Grenzen Ihrer Funktionalitäten liegen.
Im Grunde handelt es sich dabei um Screaming Architecture, das in React umgesetzt wurde – und heute ist es die gängige Wahl. In der Referenzanwendung enthält features/cart/ die Warenkorbkomponenten, useCart die Warenkorb-API sowie cartStore; features/checkout/ enthält CheckoutForm, useCheckout und checkoutApi; und so weiter, wobei Button, Modal sowie weitere Grundkomponenten in einem gemeinsamen Verzeichnis components/ abgelegt sind.
Wer verwendet es?
Die bekannteste Referenzimplementierung ist Bulletproof React von Alan Alickovic, das etwa 35.5k GitHub-Sterne hatte und zum Zeitpunkt der Erstellung noch aktiv gepflegt wurde. Es handelt sich um die am weitesten verbreitete React-Struktur, und das Dokument zur Projektstruktur von Bulletproof React lohnt es, direkt gelesen zu werden, anstatt sich auf zweithand überlieferte Zusammenfassungen zu verlassen.
Die empfohlene Struktur von src/ besteht aus genau elf obersten Verzeichnissen, die unten zusammen mit ihrer jeweiligen Funktion aufgelistet sind. Beachten Sie, dass nur eines davon, features, nach Domänen organisiert ist; die restlichen sind gemeinsame Schichten, auf die die Features zurückgreifen, sowie die app-Schicht, die alles zusammenfügt.
src
+-- app # application layer (routes, app.tsx, provider.tsx, router.tsx)
+-- assets # static files such as images, fonts, etc.
+-- components # shared components used across the entire application
+-- config # global configurations, exported env variables etc.
+-- features # feature based modules
+-- hooks # shared hooks used across the entire application
+-- lib # reusable libraries preconfigured for the application
+-- stores # global state stores
+-- testing # test utilities and mocks
+-- types # shared types used across the application
+-- utils # shared utility functions
In jeder Funktion kann es bis zu sieben Abschnitte geben: api, assets, components, hooks, stores, types und utils. In der Dokumentation wird ausdrücklich erwähnt, dass eine Funktion nur die Abschnitte benötigt, die sie tatsächlich verwendet.
Zwei Regeln leisten den größten Teil der Arbeit:
- Keine Imports zwischen Funktionen. Bulletproof React rät davon ab, dass eine Funktion eine andere importiert, und empfiehlt stattdessen die Komposition von Funktionen auf Anwendungsebene, damit jede unabhängig bleibt und der Code besser verständlich ist.
- Einweg-Fluss. Der Code sollte in eine einzige Richtung fließen, vom gemeinsamen Code zu den Funktionen und schließlich zur Anwendung (
shared -> features -> app).
Sowohl Regeln können mit der ESLint-Regel import/no-restricted-paths durchgesetzt werden. Dieser Durchsetzungs Schritt wird von vielen Teams übersprungen, und das ist in der Regel der Grund, warum ihre Grenzen im Laufe der Zeit verschwimmen.
Umkehrung der Barrel-Dateien: Bulletproof React empfahl früher Barrel-Dateien, also eine
index.js, die die öffentlichen Schnittstellen einer Funktion erneut exportiert. Heute wird empfohlen, Dateien direkt zu importieren, da Barrel-Dateien das Tree-Shaking in Vite stören und Leistungsprobleme verursachen können. Wenn Sie dieses Muster aus einem älteren Leitfaden gelernt haben, hat sich der Rat geändert. Da Barrel-Dateien früher die übliche Methode waren, um die öffentliche API einer Funktion darzustellen, bedeutet ihr Verzicht, dass die Grenzen nun vollständig in der Lint-Konfiguration definiert werden.
Vorteile
- Durch die gemeinsame Speicherung entstehen automatisch Zusammenhänge. Eine Änderung in einem Ordner betrifft nur diesen, da zusammen ändernde Dateien dort ebenfalls liegen.
rm -rf. Entfernen Sie features/checkout/, beheben Sie die Importprobleme, über die der Compiler klagt, und es ist erledigt. Das ist wichtiger, als es klingt: Code, den man nur schwer löschen kann, wird in der Regel nie gelöscht.auth/, catalog/, cart/, checkout/ und orders/ durchliest, versteht das Produkt bereits, bevor er ein einzelnes Datei öffnet.Schwächen
- „Feature“ hat keine feste Definition, weshalb es zu Streitigkeiten kommen wird.
CartTotal.jsxwird sowohl im Warenkorb als auch beim Bezahlen gerendert. Keine der Lösungen ist richtig; man muss einfach eine Entscheidung treffen, die vermutlich später noch überdacht werden muss. - Gemeinsam genutzter Code erfordert jedes Mal eine eigene Beurteilung.
useDebouncewird derzeit nur für die Katalogsuche verwendet. Wenn man es global macht, füllt sich das Verzeichnis mit gemeinsamen Hooks, die nur einmal genutzt werden. Belassen Sie es infeatures/catalog/hooks/; wenn die Rechnungsstellung es benötigt, muss man es duplizieren, in verschiedenen Features importieren (was verboten ist) oder es auf eine höhere Ebene heben. - Das Zu früh Aufteilen in Features schadet sogar noch mehr als das Zu früh Abstrahieren. Ein 40-Dateien umfassendes Projekt in fünf Feature-Verzeichnisse aufzuteilen, führt nur zu Ritualen statt zu Struktur.
Wo es versagt
In den meisten Fällen versagt sie nicht, weshalb sie standardmäßig verwendet wird. Es entstehen Probleme in zwei Situationen: Erstens, wenn eine einzelne Funktion sehr groß wird – ein Verzeichnis checkout/ mit 80 Dateien ist lediglich eine strukturelle Ebene unterhalb der Typenstruktur. Zweitens, wenn ein echtes Domänenmodell entsteht, das nicht mit den UI-Funktionen übereinstimmt. Diese beiden Faktoren führen zu den folgenden drei Strukturen.
Atomic Design
Was es ist
Atomic Design ist die Methodik von Brad Frost, die er in einem Blogbeitrag vom Juni 2013 vorstellte und in einem Buch aus dem Jahr 2016 erweiterte. Sie definiert fünf Ebenen. Um seine Definitionen zu paraphrasieren:
- Atome sind die grundlegenden Bausteine, aus denen jede Schnittstelle besteht.
- Moleküle sind recht einfache Gruppen von UI-Elementen, die als Einheit zusammenarbeiten.
- Organismen sind komplexere Komponenten, die aus Molekülen, Atomen und sogar anderen Organismen aufgebaut sind.
- Vorlagen sind seitenbezogene Strukturen, die Komponenten in eine Anordnung bringen und die zugrunde liegende Inhaltssstruktur beschreiben.
- Seiten sind konkrete Instanzen von Vorlagen, die mit echtem, repräsentativem Inhalt gefüllt sind.
Die Details, die fast alle übersehen, sind Frosts eigene Beschreibung seines Zwecks. Er präsentiert Atomic Design als mentales Modell und nicht als linearen Prozess – eine Möglichkeit, eine Schnittstelle gleichzeitig als kohärentes Ganzes und als Ansammlung von Teilen zu betrachten. Es handelte sich dabei niemals um eine Ordnerstruktur; die Gemeinschaft fügte später die Ordner hinzu.
Wer verwendet es
Hier weicht die Realität von dem Ruf ab. Es gibt kaum nachprüfbare Belege dafür, dass große React-Designsysteme tatsächlich eine strikte Ordnerstruktur mit den Namen atoms/, molecules/ und organisms/ verwenden. Shopify Polaris sowie GitHub Primer beispielsweise nutzen diese Namensgebung nicht, obwohl Suchergebnisse etwas anderes nahelegen. Ein eindeutig nachweisbares Beispiel ist Pattern Lab – Frosts eigenes Tool, das diese Methode veranschaulichen soll.
In den Reihen der Designsystem-Entwickler ist Atomic Design äußerst einflussreich. Als tatsächliche Ordnerstruktur in produktiven React-Anwendungen sind die Belege für seine Verwendung jedoch viel spärlicher, als sein Ruhm vermuten lässt. Wenn jemand behauptet, ein großes Unternehmen strukturiere seine React-Ordner auf diese Weise, sollte man um einen Einblick in das Repository gebeten werden.
Vorteile
- Es bietet Designern und Ingenieuren eine gemeinsame Sprache. Das ist der eigentliche Vorteil, wobei Ordner dabei keine Rolle spielen. Wenn ein Designer auf ein Element verweist, versteht ein Ingenieur sofort dessen groben Umfang – was in Design-System-Teams sehr wertvoll ist.
- Es fördert die Komposition. Die Hierarchie ermutigt dazu, kleine Teile zu erstellen und sie zusammenzusetzen – was eine gute React-Praxis ist, unabhängig davon, wo die Dateien liegen.
- Es macht Ihr Inventar an Primitiven sichtbar. Ein Ordner namens
atoms/macht deutlich, wenn es drei Button-Varianten gibt, die eigentlich eine sein sollten.
Schwächen
- Die Klassifizierung ist tatsächlich schwierig, und der Ersteller räumt dies ein.
Modal.jsxkombiniert einen Hintergrund, einen Container, eine Überschrift sowie ein Schließen-Element. Handelt es sich dabei um eine Gruppe von Atomen und somit um eine Molekülstruktur, oder um ein komplexes, eigenständiges Element und somit um einen Organismus? Frost veröffentlichte schließlich eine Faustregel: Wenn die Aufteilung eines Komponenten auf grundlegende Elemente wie Bilder, Überschriften, Absätze und Schaltflächen führt, handelt es sich um eine Molekülstruktur; wenn hingegen kleinere Komponenten wie Karten, Teilen-Listen oder Medienobjekte entstehen, handelt es sich um einen Organismus. Eine solche Heuristik existiert nur, weil die Grenze unklar ist – und jede unklare Grenze führt zu wiederkehrenden Diskussionen, die keinen Nutzen für den Benutzer bringen.
Button und CheckoutForm, aber keinerlei Ansichten zu useCheckout, checkoutApi, cartStore oder formatPrice. Die Hälfte der Dateien fällt außerhalb dieser Taxonomie, wodurch Teams letztendlich Atome, Moleküle und Organismen sowie ein typbasiertes Layout für alles Weitere haben – was beide Problembereiche mit sich bringt.organisms/, templates/, pages/, hooks/ und api/ verteilt.Wo es versagt
Sobald man das Design-System verlässt. Die Sortierung von Primitiven nach Komplexität innerhalb von packages/ui/ oder shared/ui/ ist sinnvoll. Wenn dies auf eine ganze Anwendung angewandt wird, versagt das Konzept sofort, da der Großteil jeder Anwendung aus Elementen besteht, die keine UI-Komponenten sind.
Frosts eigene Haltung unterstützt diese Interpretation: Er bezeichnet es als mentales Modell und nicht als Dogma und fordert die Teams auf, die Phasen umzubenennen. Teams, die in einer React-Anwendung strenge Trennung zwischen Atomen und Molekülen durchsetzen, sind strenger als der Erfinder dieser Methode.
Domain-getriebene Struktur
Was das ist
Domain-Driven Design stammt aus Eric Evans’ Buch von 2003, das mehr als ein Jahrzehnt vor der Entstehung von React verfasst wurde, und handelt von einem Ansatz zur Modellierung komplexer Geschäftsdomänen in Code. Zwei seiner Konzepte finden auch im Frontend Anwendung. Ein begrenzter Kontext ist ein Bereich des Software-Systems, in dem bestimmte Begriffe, Definitionen und Regeln konsequent gelten. Die universelle Sprache ist das Prinzip, dass der Code denselben Wortschatz wie das Geschäft verwenden sollte.
In einer React-Anwendung wird jede Domäne zu einem obersten Verzeichnis, das intern in Schichten unterteilt ist:
domain/enthält Entitäten und Wertobjekte: reine Geschäftsregeln.application/enthält Anwendungsfallbeispiele.infrastructure/enthält API-Clienten und Speicherlösungen.presentation/enthält die React-Komponenten.
Abhängigkeiten verweisen nur nach innen, sodass das Domänenmodell nichts über React oder HTTP weiß.
Betrachten Sie, was mit den unhandlichen Dateien geschieht. formatPrice ist nicht mehr ein Hilfsfunktion; es wird zu einem Money-Wertobjekt, das sich in catalog/domain/ befindet. useAuth reduziert sich auf eine schlichte Verpackungsschicht für den Anwendungsfall loginUser auf der Präsentationsebene. React wird absichtlich an die Ränder verdrängt.
Wer nutzt es
Die zuverlässigste praktische Referenz ist Alex Bespoyasovs Clean Architecture on Frontend, das eine vollständige Implementierung in React und TypeScript im Repository bespoyasov/frontend-clean-architecture bietet. Seine Erklärung der Schichtstruktur besagt, dass das Domain-Modul jener Teil ist, der unverändert bleibt, egal ob man von React zu Angular wechselt oder einen bestimmten Anwendungsfall ändert.
Ebenfalls erwähnenswert ist, dass Manfred Steyer, wohl der bekannteste Befürworter von DDD im Frontend-Bereich, hauptsächlich mit Angular arbeitet. Angulos klassenbasiertes, serviceorientiertes Modell mit intensiver Abhängigkeitsinjektion eignet sich besser für DDD als Reacts Funktionen und Hooks. Bevor man starke Leitlinien zu DDD im Frontend vollumfänglich übernimmt, sollte man prüfen, für welches Framework sie ursprünglich geschrieben wurden.
Dieser Stil kommt in der Fintech-Branche, im Versicherungswesen, im Gesundheitswesen, in der Logistik sowie bei Unternehmens-B2B-Software zum Einsatz, wo die Geschäftsregeln komplex sind und bereits lange vor der Benutzeroberfläche existierten.
Vorteile
- Die Geschäftslogik überdauert das Framework. Eine
Cart-Entität mitaddItem-Regeln ist reiner TypeScript; ein Austausch gegen React würde die Domäneschicht unberührt lassen. Bei einem Produkt, das voraussichtlich ein Jahrzehnt lang genutzt wird, stellt dies einen wertvollen Vorteil dar. - Die Regeln lassen sich ohne React testen. Es gibt nichts zu rendern, keine Hooks und keine Testbibliotheken – nur Funktionen und Aussagen, die in Millisekunden ausgeführt werden. Bei komplexen Regeln rechtfertigt allein das die verwendete Struktur.
- Der Code spiegelt die geschäftlichen Abläufe wider. Wenn ein Experte sagt, dass eine Bestellung erst versandt werden darf, nachdem die Adresse überprüft wurde, hat diese Regel genau einen Ort: eine Datei namens
Order.ts.
Schwächen
- Die notwendigen Standardstrukturen sind umfangreich und stellen die Hauptkosten dar. Jede Entität erfordert in der Regel ein Domänenmodell, ein DTO sowie einen Mapper dazwischen – insgesamt drei Dateien und eine Übersetzungsschicht für das, was eine auf Funktionen basierende Anwendung als einzige typisierte API-Antwort darstellt. Multipliziert man das mit zwanzig Entitäten, wird die Komplexität noch größer.
- Die meisten React-Anwendungen verfügen über keinen echten Domain-Modell. Das ist ein Urteil und keine belegte Tatsache, doch es ist der Schlüsselpunkt dieses Abschnitts. Die typische React-Anwendung rendernt Daten aus einer API, die von einem anderen Team oder einer anderen Dienstleistung betrieben wird, und ihre eigentliche Komplexität liegt im Serverzustand, beim Caching, bei optimistischen Updates sowie bei Rennbedingungen. DDD befasst sich mit keinem dieser Aspekte; eine Bibliothek für den Serverzustand wie React Query löst das eigentliche Problem besser als eine Domain-Schicht. Wenn Ihre Entitäten typisierte API-Antworten ohne Invarianten sind, kostet die Architektur Geld und bietet keinen Schutz.
- Die Frontend-Entwicklung kann sich nicht wirklich von ihrem Framework befreien. Versprochen wird Unabhängigkeit vom Framework, doch ein Frontend ist stets an ein Rendering-Modell, Routing-Mechanismen, Muster zur Zustandsverwaltung, Browser-APIs sowie Build-Tools gebunden. Die Saubere Architektur verlagert diese Einschränkungen an den Rand, anstatt sie zu beseitigen. Das ist ein echter Vorteil – nur geringer als in der Vermarktung dargestellt.
- UI-basierte Kontexte sind unklar definiert. Im Backend sorgen klare Datenbesitzverhältnisse für Struktur. Ein Frontend zeigt nur das an, was ein Bildschirm benötigt, und eine Seite umfasst oft drei verschiedene Kontexte – wodurch die Grenzen subjektiv und veränderlich werden.
- Die Einarbeitung dauert Zeit. Ein React-Entwickler, der noch nie mit DDD in Berührung gekommen ist, benötigt möglicherweise Wochen, um produktiv zu werden, und immer wieder wird gefragt, warum der Mapper überhaupt existiert.
Wo es versagt
Wenn das Domänenmodell nur auf dem Papier existiert. DDD ist dafür konzipiert, sich bei echter Geschäftskomplexität auszuzahlen. Wenn Ihre Invarianten real sind – wie Geld, Verträge, regulierte Arbeitsabläufe oder regelgesteuerte mehrstufige Zustandsmaschinen – lohnt sich die Investition. Wenn die Anwendung lediglich eine Produktliste über einen Endpunkt anzeigt, haben Sie viel Überfluss um das fetch-Verfahren gewickelt.
Ein nützlicher Test: Können Sie drei Geschäftsregeln nennen, die interessanter sind als eine Überprüfung erforderlicher Felder? Wenn nicht, handelt es sich um eine Benutzeroberfläche – keine echte Domäne.
Feature-Sliced Design
Was es ist
Feature-Sliced Design (FSD) ist die einzige Struktur hier, die eine formale Spezifikation darstellt – mit versionierten Releases, Dokumentation sowie einem offiziellen Architektur-Checker namens steiger. Die anderen sind lediglich Konventionen; FSD ist schriftlich festgehalten.
Es gibt drei Hierarchieebenen:
- Die Ebenen sind die festgelegten obersten Verzeichnisse:
app,pages,widgets,features,entitiesundshared. - Slices sind Gruppen innerhalb einer Ebene, die nach ihrer geschäftlichen Bedeutung unterteilt sind – also danach, was der Code für das Produkt oder die Anwendung bedeutet.
- Segments sind Gruppen innerhalb eines Slices, die nach ihrem technischen Zweck unterteilt sind:
ui,api,model,libundconfig.
Die Regel, die alles zusammenhält, besagt, dass ein Modul in einem Slice nur andere Slices importieren darf, wenn diese sich auf Ebenen befinden, die strikt unter seiner eigenen Ebene liegen. Importe verlaufen stets nach unten, und zwei Slices, die dieselbe Ebene teilen, dürfen sich niemals gegenseitig importieren. Zwischen Geschwistermodulen besteht keinerlei Kopplung – dies wird durch einen Linter durchgesetzt und nicht durch gute Absichten.
Zwei Korrekturen zu Behauptungen, die anderswo kursieren, sind erwähnenswert:
- Die Schicht
processesist veraltet. In der Dokumentation heißt es, dass die aktuelle Spezifikation davon abrät und empfiehlt, ihren Inhalt infeaturesundappzu übertragen. Anleitungen, die weiterhin sieben Schichten einschließlichprocessesaufzählen, beschreiben eine ältere Version. - Die Schicht
pageswurde nicht veraltet, sondern aufgewertet. Die im November 2024 veröffentlichte Version 2.1 trägt die Überschrift „Pages kommen zuerst!“. Die Empfehlung lautet nun, mehr Code in den Seiten zu belassen: große UI-Blöcke, Formulare sowie Datenlogik, die von keiner anderen Seite wiederverwendet wird, sollten in dem jeweiligen Seitenabschnitt verbleiben.
In der FSD-Version der Referenzanwendung befindet sich CheckoutForm.tsx in pages/checkout/ui/ und nicht in einer Feature-Slice. Der Ansatz von v2.0 hätte es aus Prinzip in Entities und Features aufgeteilt, während v2.1 es innerhalb der Seite belässt, bis eine andere Seite tatsächlich einen Teil davon benötigt.
Die Motivation für die Änderung ist der interessante Teil. Die entity-basierte Aufteilung von v2.0 führte zu schwacher Kohäsion: Um einen Benutzerfluss nachzuvollziehen, musste man durch viele Verzeichnisse gehen, und toter Code war schwer zu erkennen. Die Betreuer gaben faktisch zu, dass die frühere Version zu viele architektonische Entscheidungen erforderte, bevor überhaupt Code geschrieben wurde. Die schärfste Kritik an FSD wurde daher von seinen eigenen Autoren geäußert und adressiert. Es gebührt Anerkennung für die Bereitstellung der Korrektur, doch die Teams, die v2.0 übernommen hatten, mussten den Preis für diese Lektion zahlen.
Wer verwendet es
Das Bild der Adoption benötigt einen ehrlichen Blick. Zum Zeitpunkt der Erstellung enthält die FSD-Startseite einen Abschnitt für Unternehmen, die FSD nutzen, nennt jedoch keine konkreten Beispiele und bittet die Besucher stattdessen, ihr eigenes Unternehmen zu melden.
Die offizielle Beispielseite listet etwa 21 Projekte auf, hauptsächlich Demos und Hobby-Apps wie eine RealWorld-Kopie, ein Sudoku-Spiel, einen virtuellen Online-Shop sowie ein kleines Browserspiel. Die Einträge, die wie Produktionssoftware aussehen, sind eher unbedeutend – darunter Roke.to, Moke Smoke, Moonlogs sowie eine Chrome-Erweiterung.
Die messbaren Indikatoren sind zum Zeitpunkt der Erstellung zwar bescheiden, aber real: Etwa 297 GitHub-Repositorien verwenden das Thema feature-sliced-design, das Dokumentations-Repository hat rund 2.300 „Stars“ und der Linter etwa 388. Die Community konzentriert sich auf die russischsprachige Entwicklerwelt, wodurch die Adoption in englischsprachigen Regionen dünner erscheint, als sie tatsächlich ist.
Aus primären Quellen konnte keine große, erkennbare westliche Firma als Nutzerin von FSD bestätigt werden, und in keiner offiziellen Dokumentation wird der Schöpfer der Methodik genannt. Für etwas, das so formalisiert ist, ist das ungewöhnlich und sollte bei der Entscheidung zur Übernahme berücksichtigt werden.
Vorteile
- Es beendet Debatten über die Platzierung, da die Spezifikation bereits eine Entscheidung getroffen hat. Wohin gehört diese Datei? Die Spezifikation gibt die Antwort. In großen Teams spart das echte Zeit – und genau hier übertrifft FSD deutlich eine auf Funktionen basierende Struktur, bei der die Definitionen der Funktionen ständig neu geöffnet werden müssen.
- Die Grenzen werden von Maschinen überprüft.
steigersowie die ESLint-Konfiguration sorgen für die Einhaltung der Importregeln, und eine durchgesetzte Architektur verfällt weitaus langsamer als eine unkontrollierte Architektur. - Es eignet sich für große Teams mit geringer Koordination. Feste Schichten sowie das Fehlen von gegenseitigen Imports ermöglichen es Teams, parallel zu arbeiten, ohne in Konflikt zu geraten.
product ist etwas, das existiert; add-to-cart ist etwas, was ein Benutzer tut. Es handelt sich um verschiedene Arten von Code, und die meisten Strukturen verschwimmen diese Unterschiede.Schwächen
- Die Lernkurve ist steil, und FSDs eigene Motivationsseite erkennt dieses allgemeine Problem an, indem sie darauf hinweist, dass bestehende Ansätze eine hohe Einstiegshürde aufweisen, was die Rekrutierung erschwert. Auch FSD ist davon nicht ausgenommen: Schichten, Schnitte und Segmente sind drei Taxonomien, die man erst lernen muss, bevor man eine einzige Datei platzieren kann.
- Der Wechsel zur Version 2.1 signalisiert Unstabilität. Die primäre Unterteilungseinheit hat sich geändert, und jeder, der sich an den Spezifikationen orientiert, muss deren Anpassungen befolgen.
- Selbst die Dokumentation bezeichnet diese Methode für kleine Projekte als übertrieben und weist darauf hin, dass sie für kurzlebige Projekte oder dann unnötig ist, wenn Geschwindigkeit wichtiger ist als Codequalität.
- Es gibt nur wenige Belege für die Wirksamkeit der Adoption. Das ist in Ordnung für ein persönliches Projekt, doch schwieriger zu verkaufen, wenn Interessenten fragen, wer sonst es in der Produktion betreibt.
- Der Umstieg auf eine Legacy-Codebasis ist kostspielig. Das Hinzufügen neuer Schichten und Komponenten zu bestehendem Code ist ein umfangreiches Projekt mit geringen sichtbaren Vorteilen.
Wo es versagt
In kleinen Teams und kurzlebigen Anwendungen, wo die Formalitäten den Nutzen überwiegen. FSD richtet sich an große, langfristige Produkte mit vielen Mitwirkenden, die wirklich strenge Grenzen benötigen. Für kleinere Projekte bringt ein auf Funktionen basierendes Konzept den größten Nutzen mit einem Bruchteil der Konzepte.
Modulare Struktur und Monorepos
Was das ist
Im Gegensatz zu den anderen geht es hier nicht um die Anordnung von src/, weshalb dieses Thema zuletzt behandelt wird. Es handelt sich um eine Repository-Struktur: mehrere Anwendungen und gemeinsam genutzte Pakete in einem einzigen Repository, koordiniert durch einen Task-Runner.
In der Dokumentation von Turborepo wird die kanonische Struktur beschrieben: Legen Sie zunächst Anwendungen und Dienste in apps/ ab und alles Weitere, wie Bibliotheken und Tools, in packages/. Siehe den Turborepo-Leitfaden zu der Gestaltung eines Repositories.
Jede Anwendung in apps/ benötigt intern weiterhin eine der anderen sechs Strukturen. Ein Monorepo steht orthogonal zur Art und Weise, wie man eine einzelne Anwendung organisiert, und das Verwechseln beider Konzepte ist der häufigste Missverständnispunkt in diesem Bereich.
Zum Zeitpunkt der Erstellung dieses Textes waren die wichtigsten Tools turbo (2.10.x, etwa 73 Millionen Monatsdownloads), nx (23.x, etwa 38 Millionen) und lerna (9.x, etwa 7,5 Millionen). Überprüfen Sie die aktuellen Zahlen, bevor Sie sich darauf verlassen. Entgegen einer weit verbreiteten Annahme ist Lerna nicht veraltet. Nx übernahm im Jahr 2022 seine Verwaltung, als es Gefahr lief, nicht mehr gewartet zu werden. Es setzt weiterhin Updates aus und überlässt nun die Ausführung von Aufgaben sowie das Caching an Nx, während es sich auf Versionierung und Veröffentlichung konzentriert.
Wer verwendet es
In Diskussionen über Monorepos werden regelmäßig Google, Meta und Microsoft genannt, was irreführend ist. Es handelt sich dabei um firmenweite Monorepos, nicht um React-Monorepos, und sie sagen nichts über Ihre React-Anwendung aus.
Die Zahlen sind weiterhin interessant. Laut Berichten umfasste Googles Repository etwa eine Milliarde Dateien und 86 TB (Potvin und Levenberg, Communications of the ACM, Juli 2016). Meta beschreibt sein von Sapling verwaltetes Repository als enthaltend Dutzende Millionen von Dateien, Commit-Operationen und Branchen. Laut Brian Harry im Mai 2017 enthielt Microsofts Windows-Repository etwa 3,5 Millionen Dateien mit insgesamt 300 GB Inhalt, an denen rund 4.000 Ingenieure arbeiteten.
Jedes dieser Unternehmen musste eigene Tools entwickeln, um damit zurechtzukommen: Google schuf Piper und Bazel, Meta baute Sapling und EdenFS, und Microsoft entwickelte GVFS. Betrachten Sie dies als Kostenindikator, nicht als Empfehlung. Wenn ein Monorepo eine eigene Infrastruktur-Team benötigt, um zu funktionieren, sagt das etwas über Monorepos aus.
Die relevanten, überprüfbaren React-Beispiele sind anders:
- Vercels
next.js-Repository ist ein echtes React-Monorepo mit oberster Ebeneapps/undpackages/, pnpm-Workspaces sowie 19 Paketen, darunternext,create-next-appundeslint-config-next. - Shopify bewahrt seine React Native-Anwendungen in einem einzigen Monorepo auf, das Shop, Inbox, Point of Sale, Shopify Mobile und Local Delivery umfasst. Das Engineering-Team erklärte im September 2021, dass ein einziges Monorepo die Wartungskosten senkt, da es nur einen Prozess gibt, den man verbessern und optimieren muss, anstatt sich um viele separate Pakete und Repositorien kümmern zu müssen.
Das ist der eigentliche Nutzen eines Monorepos – und es betrifft dabei den Wartungsprozess, nicht die Codeorganisation.
Vorteile
- Änderungen zwischen Paketen sind atomar. Die Änderung von
packages/ui/Buttonsowie aller drei verwendenden Apps erfolgt in einem einzigen Commit, einer Pull Request, einer Überprüfung und einem CI-Testlauf. Bei getrennten Repositorien wären das ein Versionsanstieg, eine Veröffentlichung und drei Abhängigkeitsaktualisierungen. Deshalb gibt es Monorepos. - Das Teilen von Ressourcen erfolgt mit klaren Grenzen.
packages/uiverfügt über seine eigenepackage.json-Datei sowie eine explizite öffentliche API, und die Paketgrenze stellt die stärkste Form dar, die JavaScript bietet. - Es gibt nur eine Version von allem: Keine App bleibt bei React 18 hängen, während eine andere React 19 verwendet – es gibt nur ein Lockfile und eine einzige Quelle der Wahrheit.
- Große Refaktorisierungen werden möglich. Man kann eine API in allen Verwendern in einem einzigen Commit umbenennen, wobei der Compiler das Ergebnis überprüft.
Schwächen
- Die notwendigen Tools stellen eine echte und dauerhafte Belastung dar: Arbeitsräume, Aufgabengraphen, entferntes Caching sowie CI-Matrizen. Schon die eigenen Dokumente von Turborepo erkennen das Skalierungsproblem an, da jeder Arbeitsraum eigene Tests, Lint-Checks und Build-Aufgaben mit sich bringt – ein einzelnes Monorepo kann somit Tausende davon enthalten. Turborepo existiert, um das Problem zu lösen, das Monorepos verursachen.
- CI wird langsamer und teurer, anstatt schneller zu werden. Ohne richtig konfigurierte Erkennung von betroffenen Änderungen sowie Caching wird bei jedem Pull Request alles ausgeführt.
- Die Einarbeitung fällt schwieriger aus. Bevor ein neuer Mitarbeiter seinen ersten Commit vornimmt, muss er lernen, wie Arbeitsräume funktionieren, wie der Aufgabengraph arbeitet und wie sich das Caching verhält.
packages/ui drei Nutzer hat, birgt jede Änderung das Risiko von Rückschritten in allen drei Anwendungen. Teams reagieren oft damit, weitere Eigenschaften und Varianten hinzuzufügen, bis aus dem gemeinsam genutzten Komponenten eine Konfigurationssprache wird, die niemand versteht.Wo es versagt
Wenn es nur eine Anwendung gibt. Ein Monorepo, dessen apps/-Ordner eine einzige Anwendung enthält, zahlt den vollen Preis für die notwendigen Tools – ohne jeglichen Nutzen: Es gibt keinen Code zum Teilen und keine Änderungen, die atomar zwischen den Anwendungen vorgenommen werden könnten.
Was die Schwellenwerte angeht, so existiert keine autoritative Angabe. Turborepo und Nx veröffentlichen keine Angaben zur Teamgröße oder Anzahl der Apps, ab denen eine Adoption sinnvoll wird. Genaue Zahlen, die im Internet kursieren – wie beispielsweise eine bestimmte Anzahl von Entwicklern, die angeblich für Turborepo erforderlich sind – stammen aus minderwertigem SEO-Inhalt und nicht aus Dokumentationen oder Forschungsergebnissen der Anbieter. Jeder, der einen genauen Schwellenwert angebt, erfindet ihn einfach.
Eine vernünftige Faustregel, die eher als Meinung denn als Fakt dargestellt wird: Nutzen Sie ein Monorepo, wenn Sie zwei echte Anwendungen haben, die tatsächlich Code teilen, oder eine einzelne Anwendung zusammen mit einem Design-System, das von externen Teams genutzt wird. Unter diesen Bedingungen übersteigen die Kosten die Vorteile – Ihre eigene Situation kann jedoch diese Grenze verschieben.
React Server Components vorgeben keine Ordnerrichtlinien
Viele aktuelle Artikel behaupten, dass React Server Components die Strukturierung von Verzeichnissen verändern, indem sie in der Regel getrennte Verzeichnisse components/server/ und components/client/ empfehlen. In der offiziellen Dokumentation findet sich jedoch nichts, das dies stützt.
Die use client-Direktive markiert eine Grenze im Modulgraphen, nicht im Verzeichnisbaum. Die entsprechende Next.js-Bereitstellungsanleitung beschreibt use client als Deklaration einer Grenze zwischen den Modulgraphen von Server und Client und erklärt, dass alle Importe sowie die direkt renderierten Unterkomponenten einer Datei, die diese Direktive enthält, schließlich im Client-Bundle landen.
Dieser zweite Punkt ist der Schlüssel. Die Grenze breitet sich über Importe aus, nicht über Ordner. Eine Datei innerhalb eines client/-Verzeichnisses ist aufgrund ihrer Lage keine Client-Komponente, und eine Datei innerhalb von server/ erhält durch diesen Namen keinen Schutz. Was das Endergebnis bestimmt, ist die Direktive zusammen mit der Kette der Importe.
Falls Sie Durchsetzung wünschen, dienen die server-only- und client-only-npm-Pakete dazu, den Build abzubrechen, wenn Code die falsche Grenze überschreitet. Ordner bieten das nicht.
Die offiziellen Richtlinien deuten eigentlich in die entgegengesetzte Richtung: 'use client' sollte weiter nach unten im Baum, zu den Endkomponenten, verlagert werden, um das Client-Bundle klein zu halten. Eine Trennung auf oberster Ebene in Server- und Client-Ordner fördert das gegenteilige Vorgehen.
Mehrere Konventionen des App Router beeinflussen tatsächlich die Organisation, und sie sind real vorhanden:
- Routengruppen, die als
(Ordner)geschrieben werden, organisieren Routen, ohne die URL zu ändern. - Private Ordner, die als
_Ordnergeschrieben werden, schließen sich vollständig der Routenverwaltung aus. - Die Zusammenlegung von Inhalten ist standardmäßig sicher, da eine Route erst dann öffentlich zugänglich wird, wenn eine
page.js- oderroute.js-Datei hinzugefügt wird. Komponenten, Tests und Hilfsfunktionen können direkt innerhalb von Routensegmenten platziert werden, ohne selbst zu Routen zu werden; daher sind für diese Zwecke private Ordner nicht erforderlich.
Next.js macht außerdem klar, dass es keine Vorgaben bezüglich der Organisation und Zusammenlegung von Projektdateien gibt. In seiner Dokumentation werden drei Organisationsstrategien ohne Rangfolge vorgestellt.
Wie man wählt
Hier ist die Entscheidung in komprimierter Form zusammengefasst, mit den wichtigsten Überlegungen:
- Weniger als etwa 30 Dateien – oder Sie erkunden das Problem noch: Bleiben Sie bei einer einfachen Struktur. Eine auf tatsächlichen Änderungsmustern basierende Struktur ist besser als eine bereits am ersten Tag vermutete, wie es auch die alten React-Dokumentationen vorschlugen.
- Unter etwa 50 Dateien, ein kleines Team und eine kurze Lebensdauer: Eine typenbasierte Struktur ist in Ordnung. Sie ist zwar nicht mehr im Trend, doch das spielt keine Rolle; die Migration würde mehr kosten, als sie einspart.
- Für alles Reale und Langzeitige: Wählen Sie eine funktionenbasierte Struktur. Das sollte die Standardlösung sein. Sie erfüllt die Anforderungen der Screaming Architecture, berücksichtigt die Zusammenlegung von Komponenten, macht das Löschen von Funktionen einfach und unterstützt grenzenorientierte Kontrollen durch Lint-Tools. Beginnen Sie mit den elf Verzeichnissen von Bulletproof React, fügen Sie frühzeitig
import/no-restricted-paths-Zonen hinzu und verzichten Sie auf Barrel-Dateien.
apps/ und packages/, das von Turborepo oder Nx gesteuert wird, und lassen Sie jede Anwendung aus dieser Liste ihre eigene interne Struktur wählen.Eine Regel hat Vorrang vor all diesen Ansätzen: Die Methoden werden kombiniert. Reale Codebasen mischen sie miteinander, und die Legacy-Dokumente von React bestätigen dies, indem sie darauf hinweisen, dass größere Projekte in der Praxis oft eine Mischung aus verschiedenen Ansätzen verwenden – daher ist es anfangs nicht besonders wichtig, den „richtigen“ Ansatz auszuwählen.
Eine völlig vernünftige Produktionsumgebung ist ein Monorepo, das anwendungsbezogene Komponenten enthält, ein Design-System-Paket, das grob nach atomaren Einheiten organisiert ist, sowie eine Domänen-Schicht für die Bereiche mit tatsächlichen Geschäftsregeln. Niemand bewertet Ihre Architektur nach ihrer Reinheit.
Kernpunkte
Die Personen, die diesen Konventionen am nächsten stehen, halten sich nicht streng daran und überarbeiten sie ständig öffentlich. Frost bezeichnet Atomic Design als mentales Modell und nicht als Dogma. In Reacts Dokumentation wurde die Entscheidungszeit auf fünf Minuten begrenzt, anschließend wurde die Seite entfernt. Next.js und Vite weigern sich, eine Stellung zu beziehen. FSD änderte in Version 2.1 seine Hauptunterteilungseinheit, und Bulletproof React änderte seinen Ratschlag bezüglich der Barrel-Dateien. Sie änderten ihre Richtung, weil sie auf echte Rückmeldungen hörten anstatt eine Position zu verteidigen.
Das lässt zwei beständige Regeln übrig, beide älter als jede hier besprochene Layout-Strategie:
- Screaming Architecture: Die obersten Verzeichnisse sollten einem neuen Entwickler mitteilen, was das Produkt tut – nicht, welches Framework es verwendet.
- Colocation: Dinge, die zusammen ändern, sollten so nah beieinander liegen, wie es vernünftig ist.
Jede Struktur in diesem Leitfaden versucht, auf einer bestimmten Ebene diese beiden Regeln einzuhalten. Wählen Sie das günstigste Angebot, das für Ihre aktuelle Anwendung diese Anforderungen erfüllt – nicht das, das Sie sich für die Zukunft wünschen. Die Anzeichen dafür, dass es Zeit ist, weiterzumachen, sind klar definiert: Dateinamenspräfixe als Ersatz für Ordnernamen, vier offene Ordner pro Änderung oder eine Stunde Verschwendung mit der Entscheidung, ob etwas eine Molekül ist. Handeln Sie auf diese Anzeichen hin, sobald sie auftauchen – und nicht früher.