Blog

Frontend-Architektur ernst nehmen mit dependency-cruiser

Ruben Oostinga

Aktualisiert Oktober 15, 2025
7 Minuten

Mit dependency-cruiser können Sie erzwingen, welche Importe erlaubt sind. So können Sie eine Architektur-Fitnessfunktion erstellen, die sicherstellt, dass Ihr Code weiterhin dem ursprünglichen Entwurf entspricht. Außerdem können Sie Ihre Abhängigkeiten visualisieren, um die tatsächliche Struktur Ihres Codes besser zu verstehen. So können Sie ihn mit Ihrem mentalen Modell vergleichen und bei Bedarf Verbesserungen vornehmen.

Ein Entwurf der Anwendungsarchitektur definiert eine Ordnerstruktur und legt fest, welche Dateien aus anderen Dateien importiert werden können. Für das Backend gibt es Entwurfsmuster wie die Schichtenarchitektur, die hexagonale Architektur (oder Ports und Adapter). Für das Frontend gibt es die klassische Model-View-Controller-Architektur. Moderne komponentenbasierte Frameworks bieten auch ihre eigenen Ansätze wie Seiten-, Funktions- oder technische Ordner. Oftmals gibt es auch einen gemeinsamen oder gemeinsamen Ordner.

Die Anwendung eines Architekturmusters oder einer Ordnerstruktur wirkt sich darauf aus, wie der Code vom Rest der Codebasis importiert werden sollte. Die manuelle Überprüfung der Korrektheit von Importen kann eine Herausforderung sein und wird bei Code-Reviews oft übersehen. Eine automatisierte Überprüfung kann nicht nur für das aktuelle Team, sondern auch für zukünftige Entwickler von Vorteil sein. Selbst wenn Sie die Anwendungsarchitektur entwerfen und den Code implementieren, kann es leicht passieren, dass Sie ungewollt von Ihren eigenen Richtlinien abweichen. Ein einziger automatischer Import aus einem Editor kann Ihr Design gefährden. Die Zusammenarbeit mit anderen vergrößert diese Herausforderung.

Seitenbezogene Ordner isoliert halten

Sie möchten nicht, dass Module aus einem seitenbezogenen Ordner Module aus einem anderen seitenbezogenen Ordner importieren. Wenn es eine seitenübergreifende Abhängigkeit gibt, bedeutet dies wahrscheinlich, dass einige Module tatsächlich gemeinsam genutzte Module sind, die versehentlich in einem seitenbezogenen Ordner platziert wurden. Die Lösung besteht darin, das Modul in einen gemeinsam genutzten oder gemeinsamen Ordner zu verschieben.

In unserer Codebasis waren wir mit diesem Problem konfrontiert, weil wir eine Seite erstellt hatten und später eine ähnliche Seite konstruierten, wobei wir feststellten, dass wir einige Komponenten wiederverwenden konnten. Wir vergaßen, die Komponenten in den gemeinsamen Ordner zu verschieben, und dieses Versehen wurde bei der Überprüfung des Codes nicht bemerkt. Mit dependency-cruiser wurde das Problem leicht entdeckt und wird in Zukunft vermieden. Siehe Isolieren von Peer-Ordnern voneinander

Verwaisten Code finden

Wenn ungenutzter Code nicht entfernt wird, sobald er nicht mehr benötigt wird, kann es schwierig sein, ihn später zu entdecken. Insbesondere bei Komponenten, die von Unit-Tests oder Storybook-Stories importiert werden, kann es vorkommen, dass sie scheinbar verwendet werden, aber in Wirklichkeit nirgendwo in der Anwendung enthalten sind.

Dependency-cruiser kann erkennen, ob der Code vom eigentlichen Produktionscode verwendet wird oder nicht. Natürlich können Ausnahmen für Konfigurationsdateien gemacht werden, die gar nicht Teil der Anwendung sein sollen.

Wir haben eine ungenutzte Komponente gefunden, komplett mit Tests und Storybook-Geschichten. Wenn wir sie entfernen, brauchen wir den Code nicht mehr zu pflegen und es gibt keine Verwirrung mehr darüber, warum der Code überhaupt vorhanden war.

Siehe Wird ein Modul tatsächlich verwendet?

Gemeinsamer Code

Es ist hilfreich, zwischen Code zu unterscheiden, der tatsächlich von verschiedenen Teilen gemeinsam genutzt wird, und Code, der gemeinsam genutzt werden könnte, aber derzeit nicht genutzt wird. Die Art und Weise, wie der Code geschrieben ist, lässt oft auf Annahmen über seine Verwendung schließen.

Indem Sie zählen, wie viele Dateien von einem Modul abhängen, können Sie feststellen, ob ein Modul als gemeinsam genutzt gelten sollte. Mit dieser Information können Sie erzwingen, dass gemeinsam genutzte Module in einem common/ oder shared/ Ordner abgelegt werden. Umgekehrt können Sie sicherstellen, dass der gesamte Code in den gemeinsam genutzten Ordnern auch tatsächlich gemeinsam genutzt wird. Wenn Sie einen Import für ein gemeinsam genutztes Modul entfernen, erhalten Sie eine Fehlermeldung und können das Modul in einen seitenbezogenen Ordner verschieben.

Hier fanden wir auch Code, der gemeinsam genutzt werden konnte oder genutzt worden war, aber in Wirklichkeit für eine einzelne Seite bestimmt war. Also haben wir diesen Code aus dem gemeinsamen Ordner in den Seitenordner verschoben. Durch diese Verschiebung wurde der Seitencode kohärenter und seine Kopplung mit dem gemeinsam genutzten Code verringert - beides wichtige architektonische Eigenschaften.

Siehe Wird ein Utility-Modul gemeinsam genutzt?

Visualisierung

Dependency-cruiser verfügt über fortschrittliche integrierte Visualisierungsfunktionen.

Wenn Sie die Abhängigkeiten zwischen verschiedenen Ordnern visualisieren, können Sie sehen:

  • Abhängigkeiten, die nicht vorhanden sein sollten
  • Ob eine Abhängigkeit ein dynamischer Import ist (für träges Laden) oder nicht
  • Zirkuläre Abhängigkeiten
  • Welche Ordner haben gemeinsamen Code
  • Wenn der Code von Drittanbietern getrennt gehalten oder überall verteilt wird

Das ist auch praktisch für die Dokumentation, damit alle das gleiche Verständnis haben.

Für eine optimale Visualisierung müssen Sie die Konfiguration anpassen, um die richtige Menge an Details zu erhalten.

In der Visualisierung unten wollen wir zeigen, dass Seiten nicht von anderen Seiten abhängen, sondern nur von der gemeinsamen Ebene und der Dienstebene. Die Dienstebene selbst ist nicht von ansichtsspezifischen Ordnern abhängig. Was nicht angezeigt wird, aber von dependency-cruiser erzwungen wird, ist, dass der Ordner services keine React-spezifischen Abhängigkeiten hat.

Real World React Codebase visualisiert mit dependency-cruiser
Real World React Codebase visualisiert mit dependency-cruiser

Vergleich mit eslint-plugin-imports

Wenn Sie dependency-cruiser mit eslint-plugin-import vergleichen, wird eine deutliche Überschneidung zwischen den beiden Tools deutlich. Beide Tools können zirkuläre Abhängigkeiten erkennen, bestimmte Importe einschränken und dabei helfen, den Testcode vom Produktionscode zu trennen. Der dependency-cruiser bietet jedoch zusätzliche Funktionen. Es kann verwaiste Dateien identifizieren, auch wenn sie von Tests importiert werden. Er erleichtert die Trennung von Geschwisterordnern, damit der Code übersichtlich bleibt. Außerdem zeigt dependency-cruiser die Verwendungshäufigkeit einer Komponente an, so dass die Teams gemeinsam genutzte Teile der Codebasis erkennen können.

Ein deutlicher Vorteil von eslint ist seine hervorragende Editor-Integration. Im Gegensatz zu dependency-cruiser zeigt eslint rote Linien unter ungültigen Importen an und gibt damit sofortiges Feedback. Wenn eine Rückmeldung im Editor wichtig ist, leistet eslint mit seiner Regel gute Dienste bei der Sicherstellung einer ordnungsgemäßen Kapselung von Drittparteien. Während dependency-cruiser eine ähnliche Leistung erbringen kann, gibt es normalerweise erst nach einem fehlgeschlagenen CI-Build Feedback, während eslint den Entwicklern sofort hilft.

Die von dependency-cruiser gebotene Visualisierung ist unbestreitbar eine einzigartige Funktion, die in einem typischen Linter fehlt.

Ich empfehle Ihnen, beide Tools zu kombinieren, um ein Entwicklererlebnis zu erreichen, das den Bedürfnissen Ihres Teams entspricht.

Erfahrung in der realen Welt

Unsere fast dreimonatige Erfahrung mit dependency-cruiser war überwältigend positiv. Als wir die Regeln aktivierten, entdeckten wir zahlreiche falsche Importe und verlegte Dateien, sogar in unserer hochwertigen, relativ neuen React-Codebasis. Diese Enthüllungen führten zu Verbesserungen bei architektonischen Qualitäten wie der Trennung von Belangen, Kapselung und Modularität. Die Korrekturen betrafen in erster Linie die Verlagerung von Dateien und die Verschiebung von Code zwischen Modulen. Dadurch, dass wir nicht alle Regeln von Anfang an durchgesetzt haben, konnten wir schrittweise Verbesserungen vornehmen, was vor allem bei größeren Codebasen von Vorteil ist.

Sobald wir damit begonnen hatten, die Regeln durchzusetzen, gab es dank unserer konsistenten Codestruktur und der etablierten Muster kaum noch Abhängigkeitsfehler. Aufgetretene Fehler wurden korrekt und mit unkomplizierten Lösungen behoben, was uns darin bestärkte, dass unser Design intakt bleibt und das Risiko einer inkrementellen Verschlechterung oder, noch krasser, eines Todes durch tausend Schnitte verhindert.

Die Visualisierung ist von unschätzbarem Wert, denn sie spiegelt die aktuellen Realitäten und nicht idealisierte mentale Modelle wider. Sie hilft bei der Einarbeitung neuer Mitglieder und, was noch wichtiger ist, ermöglicht es bestehenden Teammitgliedern, architektonische Verbesserungen zu entdecken.

Fazit

Wachsamkeit in der Softwarearchitektur ist während des gesamten Entwicklungszyklus entscheidend, um einer inkrementellen Verschlechterung entgegenzuwirken; dies gilt sowohl für Frontend- als auch für Backend-Codebasen. Dependency-cruiser vereinfacht den Design-Review-Prozess und gewährleistet die Durchsetzung des Designs während der Entwicklung.

Leider ist die Produktionscodebasis, in der ich dependency-cruiser eingesetzt habe, nicht öffentlich. Allerdings habe ich während eines Xebia Innovation Day einige Experimente durchgeführt. Diesen Code finden Sie auf GitHub.

Verfasst von

Ruben Oostinga

Contact

Let’s discuss how we can support your journey.