Organisation großer Frontends: Das LIFT vs. Feature-Sliced Design Dilemma
Lukas Schneider
DevOps Engineer · Leapcell

Einleitung
Mit zunehmender Komplexität und Größe von Frontend-Anwendungen wird die Art und Weise, wie wir unseren Code organisieren, von entscheidender Bedeutung. Ohne eine klar definierte Struktur können Projekte schnell zu einem unüberschaubaren Durcheinander werden, was zu langsameren Entwicklungszyklen, erhöhten Fehlern und einer steilen Lernkurve für neue Teammitglieder führt. Zwei herausragende Architekturmuster sind entstanden, um diese Herausforderungen anzugehen: LIFT (Locating Logic Intuitively Fast) und Feature-Sliced Design (FSD). Beide bieten unterschiedliche Ansätze für Modularität und Wartbarkeit, bedienen jedoch unterschiedliche Philosophien und Projektanforderungen. Dieser Artikel wird LIFT und FSD dekonstruieren und ihre Prinzipien, Implementierungsstrategien und geeigneten Anwendungsfälle vergleichen, um Ihnen bei der fundierten Entscheidung für Ihr nächstes groß angelegtes Frontend-Projekt zu helfen.
Kernkonzepte erklärt
Bevor wir uns mit den Feinheiten von LIFT und FSD befassen, lassen Sie uns einige grundlegende Begriffe klären, die für diese Diskussion zentral sind.
- Modularität: Der Grad, in dem die Komponenten eines Systems getrennt und neu kombiniert werden können. Hohe Modularität bedeutet, dass Komponenten unabhängig und austauschbar sind, was Entwicklung und Wartung erleichtert.
- Trennung der Belange (Separation of Concerns): Das Prinzip, ein Computerprogramm in verschiedene Funktionen zu unterteilen, die sich in ihrer Funktionalität so wenig wie möglich überschneiden. Jede Komponente oder jedes Modul sollte idealerweise nur einen spezifischen Belang behandeln.
- Kapselung (Encapsulation): Die Bündelung von Daten mit den Methoden, die diese Daten verarbeiten, und die Einschränkung des direkten Zugriffs auf einige Teile der Komponente. Dies wird oft durch Datei- oder Ordnergrenzen erreicht.
- Domänengetriebenes Design (Domain-driven Design - DDD): Ein Ansatz zur Softwareentwicklung, der sich auf die Definition eines Domänenmodells konzentriert. Im Frontend bedeutet dies, Code um Geschäftskonzepte herum zu organisieren und nicht um technische Belange.
- Skalierbarkeit: Die Fähigkeit eines Systems, mit einer wachsenden Arbeitsmenge umzugehen, oder sein Potenzial, erweitert zu werden, um dieses Wachstum zu bewältigen. In der Codeorganisation bedeutet dies, dass das Muster eine große Anzahl von Dateien und Funktionen effizient unterstützen kann, ohne unhandlich zu werden.
LIFT: Locating Logic Intuitively Fast
Prinzipien und Philosophie
LIFT, ein Akronym für Locating Logic Intuitively Fast, ist eine Reihe von Richtlinien, die von Angular populär gemacht wurden. Seine Kernphilosophie dreht sich darum, es Entwicklern leicht zu machen, relevante Codeschnipsel schnell zu finden, unabhängig von der Projektgröße. LIFT schlägt vier Hauptregeln vor:
- Locate (Finden): Dateien für eine bestimmte Funktion sollten an einem einzigen, intuitiven Ort platziert werden. Dies bedeutet normalerweise, dass Dateien, die mit einer Funktion zusammenhängen, in ihrem eigenen Verzeichnis gruppiert werden.
- Identify (Identifizieren): Die Namen von Dateien sollten sofort angeben, was sie enthalten. Zum Beispiel identifiziert
user-list.component.ts
eindeutig eine Datei als Angular-Komponente für eine Benutzerliste. - Flat (Flach): Halten Sie Ordnerstrukturen so flach wie möglich, bis eine Funktion erheblich wächst. Dies vermeidet tiefe Verschachtelungen, die die Navigation umständlich machen können.
- Try to be DRY (Don't Repeat Yourself - Vermeiden Sie Wiederholungen): Vermeiden Sie redundanten Code.
Das Hauptziel von LIFT ist die Entwicklererfahrung und Auffindbarkeit. Wenn ein Entwickler an einer bestimmten Funktion arbeiten muss, sollte er schnell zu ihrem dedizierten Ordner navigieren und alle zugehörigen Dateien finden können.
Implementierung und Beispiel
In einer LIFT-basierten Struktur sehen Sie normalerweise Funktionen, die in Verzeichnissen auf oberster Ebene gruppiert sind. Innerhalb jedes Funktionsverzeichnisses würden Sie alle zugehörigen Komponenten, Dienste, Modelle und Tests finden.
Betrachten Sie eine einfache E-Commerce-Anwendung. Eine LIFT-Struktur könnte so aussehen:
src/
├── app/
│ ├── core/ // Anwendungsweite Dienste, Interceptoren usw.
│ │ ├── auth/
│ │ │ ├── auth.service.ts
│ │ │ └── ...
│ │ └── ...
│ ├── shared/ // Wiederverwendbare UI-Komponenten, Hilfsfunktionen
│ │ ├── components/
│ │ │ ├── button/
│ │ │ │ ├── button.component.ts
│ │ │ │ └── button.component.html
│ │ │ └── ...
│ │ └── pipes/
│ │ └── ...
│ ├── features/ // Hauptgeschäftsfunktionen
│ │ ├── product/ // 'product'-Funktion
│ │ │ ├── components/
│ │ │ │ ├── product-card/
│ │ │ │ │ ├── product-card.component.ts
│ │ │ │ │ └── product-card.component.html
│ │ │ │ └── product-list/
│ │ │ │ ├── product-list.component.ts
│ │ │ │ └── product-list.component.html
│ │ │ ├── services/
│ │ │ │ └── product.service.ts
│ │ │ ├── models/
│ │ │ │ └── product.model.ts
│ │ │ ├── product.module.ts
│ │ │ └── product.routes.ts
│ │ ├── cart/ // 'cart'-Funktion
│ │ │ ├── components/
│ │ │ │ ├── cart-item/
│ │ │ │ └── cart-view/
│ │ │ ├── services/
│ │ │ └── ...
│ │ └── user/
│ │ └── ...
│ └── app.component.ts
│ └── app.module.ts
│ └── app.routes.ts
└── environments/
└── main.ts
In diesem Beispiel befinden sich alle für die product
-Funktion relevanten Dateien (Komponenten, Dienste, Modelle, Routing) im Verzeichnis src/app/features/product
. Dies erleichtert die intuitive Auffindbarkeit aller relevanten Codeschnipsel bei der Arbeit an produktbezogenen Funktionen.
Anwendungsbereiche
LIFT ist besonders gut geeignet für:
- Kleine bis mittelgroße Anwendungen: Bei denen die Anzahl der Funktionen überschaubar ist und Entwickler die Gesamtstruktur leicht im Kopf behalten können.
- Teams, die schnelle Funktionsnavigation priorisieren: Wenn die Geschwindigkeit, mit der Code gefunden wird, für die Entwicklerproduktivität von größter Bedeutung ist.
- Projekte mit klaren Domänengrenzen: Sobald Funktionen definiert sind, sind alle ihre internen Teile eng gekoppelt.
Mit wachsender Komplexität der Anwendungen können die Grenzen zwischen Funktionen jedoch anfangen zu verschwimmen, und die Verwaltung von Abhängigkeiten zwischen Funktionsmodulen kann schwierig werden. Gemeinsam genutzte Komponenten oder Dienste können in verschiedenen Funktionsordnern landen, oder ein "shared"-Ordner kann zu einer Sammelstelle werden.
Feature-Sliced Design (FSD)
Prinzipien und Philosophie
Feature-Sliced Design (FSD) verfolgt einen eigenwilligeren und strukturierteren Ansatz zur Codeorganisation mit dem Ziel, eine hoch skalierbare und wartbare Architektur für große und komplexe Anwendungen zu schaffen. FSD führt das Konzept von "Slices" und "Layers" mit strengen Regeln für die Kommunikation zwischen den Ebenen und die Abhängigkeitsverwaltung ein. Seine Kernprinzipien sind:
- Layers (Ebenen): Die Anwendung ist in horizontale Ebenen unterteilt, die jeweils eine spezifische Verantwortung tragen (z. B.
app
,pages
,widgets
,features
,entities
,shared
). - Slices (Scheiben): Innerhalb jeder Ebene sind Komponenten in "Slices" gruppiert, die einen in sich geschlossenen Teil der Anwendungsdomäne darstellen (z. B.
product-card
,user-profile
). - Vertikale Kohäsion, Horizontale Isolation: Code, der sich auf eine bestimmte Funktion oder Slice bezieht, wird vertikal über Ebenen hinweg gruppiert, während verschiedene Slices innerhalb derselben Ebene voneinander isoliert sind.
- Strikte Abhängigkeitsregeln: Höhere Ebenen können von niedrigeren Ebenen abhängen, aber nicht umgekehrt. Dieser unidirektionale Fluss verhindert zirkuläre Abhängigkeiten und fördert eine robuste Architektur.
- Public API (Öffentliche API): Jede Slice sollte eine gut definierte öffentliche API bereitstellen, um die Kapselung aufrechtzuerhalten und externe Interaktionen zu steuern.
FSD betont architektonische Klarheit, Kapselung und Skalierbarkeit als seine Hauptziele. Es zielt darauf ab, die Code-Entropie zu verhindern, indem es strenge Regeln durchsetzt, was die Bewältigung der Komplexität mit wachsender Anwendung erleichtert.
Implementierung und Beispiel
FSD organisiert ein Projekt typischerweise in eine hierarchische Struktur, die auf Ebenen basiert, und dann weiter in Slices innerhalb dieser Ebenen.
src/
├── app/ // Anwendungsweite Logik, Routing, globale Stile
│ ├── providers/ // Globale Kontext-Provider, z. B. Redux-Store, Auth-Kontext
│ │ ├── store.ts
│ │ └── auth.ts
│ ├── layouts/ // Kernlayouts (z. B. Header, Footer, Sidebar)
│ │ ├── base-layout/
│ │ │ ├── index.ts
│ │ │ └── ui.tsx
│ │ └── ...
│ ├── index.ts
│ └── styles/
│ └── global.css
├── pages/ // Vollständige Seitenkomponenten, Zusammensetzung aus Features/Widgets
│ ├── home/
│ │ ├── index.ts
│ │ └── ui.tsx
│ ├── product-details/
│ │ ├── index.ts
│ │ └── ui.tsx
│ └── profile/
│ └── ...
├── widgets/ // Kombinationen aus verwandten Features/Entities, wie ein Dashboard-Widget
│ ├── product-list-widget/
│ │ ├── index.ts
│ │ └── ui.tsx
│ └── user-profile-card/
│ └── ...
├── features/ // Konkrete Geschäftslogik mit spezifischen Interaktionen (z. B. "add-to-cart")
│ ├── add-to-cart/
│ │ ├── index.ts // Öffentliche API für die Funktion wie `addToCartModel`
│ │ ├── api/ // API-Aufrufe
│ │ ├── model/ // Redux-Slice, Zustandsverwaltung
│ │ ├── ui.tsx // UI-Komponente, kann von `model` abhängen
│ │ └── lib/ // Hilfsprogramme
│ ├── sign-in/
│ │ └── ...
│ └── filter-products/
│ └── ...
├── entities/ // Domänenspezifische Entitäten wie "product", "user", "order"
│ ├── product/
│ │ ├── index.ts // Öffentliche API für die Entität wie `productModel`
│ │ ├── api/ // Produktbezogene API-Aufrufe
│ │ ├── model/ // Redux-Slice, Zustandsverwaltung für Produktdaten
│ │ ├── ui.tsx // UI-Komponente zur Anzeige von Produktdaten (z. B. nackener Produktartikel)
│ │ └── lib/ // Hilfsprogramme
│ ├── user/
│ │ └── ...
│ └── ...
├── shared/ // Wiederverwendbarer und generischer Code (UI-Komponenten, Hilfsprogramme, Konfiguration, Bibliothek)
│ ├── ui/ // Generische UI-Komponenten (z. B. Button, Input)
│ │ ├── button/
│ │ │ ├── index.ts
│ │ │ └── ui.tsx
│ │ └── input/
│ │ └── ...
│ ├── lib/ // Hilfsfunktionen (z. B. date-formatter)
│ │ └── formatters.ts
│ ├── config/ // Anwendungsweite Konstanten
│ │ └── apiUrl.ts
│ └── api/ // Generelles API-Hilfsprogramm
│ └── baseApi.ts
└── index.tsx // Einstiegspunkt
In diesem FSD-Beispiel integriert die product-details
page
die product
entity
(zur Datenanzeige) und eine add-to-cart
feature
(für Interaktion).
Die add-to-cart
-Funktion hängt von der product
-Entität (um zu wissen, was hinzugefügt werden soll) und potenziell von shared/ui
für eine Schaltfläche ab.
Abhängigkeiten fließen immer nach unten: pages
können widgets
, features
, entities
und shared
verwenden. features
können entities
und shared
verwenden. entities
können nur shared
verwenden. Diese strenge Regel verhindert, dass Logik auf höherer Ebene in niedrigere Ebenen eindringt und klare Grenzen aufrechterhält.
Anwendungsbereiche
FSD ist sehr effektiv für:
- Große und extrem komplexe Anwendungen: Wo langfristige Wartbarkeit und Skalierbarkeit von größter Bedeutung sind.
- Große Teams: Bei vielen Entwicklern, die gleichzeitig an verschiedenen Teilen der Anwendung arbeiten, minimieren die strengen Regeln von FSD Konflikte und gewährleisten architektonische Konsistenz.
- Projekte, die hohe architektonische Strenge erfordern: Wenn die Vermeidung technischer Schulden und die Durchsetzung einer sauberen Architektur oberste Priorität haben.
- Anwendungen mit sich entwickelnden Anforderungen: Die Modularität ermöglicht die einfachere Hinzufügung, Entfernung oder Änderung von Funktionen ohne signifikante Dominoeffekte.
Der Overhead für das Erlernen der Konventionen von FSD und die Einrichtung seiner strengen Struktur kann anfangs höher sein, was es für sehr kleine, schnell prototypisierte Anwendungen weniger geeignet macht, wo dieses Maß an Strenge nicht gerechtfertigt ist.
Fazit
Sowohl LIFT als auch Feature-Sliced Design bieten wertvolle Ansätze zur Organisation großer Frontend-Projekte, jeder mit seinen Stärken und Kompromissen. LIFT priorisiert die Intuition der Entwickler und die schnelle Code-Auffindbarkeit, was es hervorragend für kleinere bis mittelgroße Anwendungen oder Teams macht, die schnelle Funktionsnavigation schätzen. Im Gegensatz dazu bietet Feature-Sliced Design ein hochstrukturiertes und skalierbares Framework mit strengen Abhängigkeitsregeln, ideal für große, komplexe Anwendungen und große Teams, bei denen langfristige Wartbarkeit und architektonische Integrität entscheidend sind. Die Wahl zwischen LIFT und FSD hängt letztendlich von der Größe Ihres Projekts, der Teamstruktur und den langfristigen architektonischen Zielen ab. Durch das Verständnis ihrer Kernprinzipien können Sie das Muster auswählen, das Ihr Team am besten dazu befähigt, robuste und wartbare Frontend-Anwendungen zu erstellen.