So schreiben Sie jedes Mal robuste Apps mit „The Clean Architecture“

Als Entwickler können wir die Verwendung externer Bibliotheken und Frameworks in unseren Systemen nicht verhindern. Die Hände der Community bilden wunderbare Werkzeuge, und ihre Verwendung ist nur natürlich. Allerdings hat alles einen Nachteil.

Unvorsichtige Teams und Einzelpersonen können in eine gefährliche Situation geraten, indem sie ihre Systeme anhand der von ihnen verwendeten Tools strukturieren. Geschäftsregeln werden mit Implementierungsdetails verwechselt. Dies kann zu einem spröden System führen, das schwer zu erweitern und zu warten ist. Was eine schnelle Änderung in der Benutzeroberfläche sein sollte, wird schließlich zu einer stundenlangen Fehlersuche. Es muss aber nicht so sein.

Die Softwarearchitektur schlägt Modelle und Regeln vor, um die Strukturen (wie Klassen, Schnittstellen und Strukturen) in einem System und deren Beziehung zueinander zu bestimmen. Diese Regeln fördern die Wiederverwendbarkeit und die Trennung von Bedenken in Bezug auf diese Elemente. Dies erleichtert das Ändern von Implementierungsdetails wie dem DBMS oder der Front-End-Bibliothek. Refaktoren und Fehlerkorrekturen betreffen so kleine Teile des Systems wie möglich. Das Hinzufügen neuer Funktionen wird zum Kinderspiel.

In diesem Artikel werde ich ein 2012 von Robert C. Martin, Onkel Bob, vorgeschlagenes Architekturmodell erläutern. Er ist Autor von Klassikern wie Clean Code und The Clean Coder. Im Oktober dieses Jahres wird er ein weiteres Buch mit dem Titel "Saubere Architektur" herausbringen.

Das Modell hat denselben Namen wie das Buch und basiert auf einfachen Konzepten:

Teilen Sie die Systemzusammensetzung in Ebenen mit eindeutigen und genau definierten Rollen ein. Und beschränken Sie die Beziehungen zwischen Entitäten in verschiedenen Ebenen. Es ist nichts Neues, Ihre Anwendung in Ebenen aufzuteilen. Aber ich habe diesen Ansatz gewählt, da er am einfachsten zu erfassen und auszuführen war. Und es macht das Testen von Use Cases kinderleicht.

Wir müssen nur sicherstellen, dass die Interactors ordnungsgemäß funktionieren, und es kann losgehen. Machen Sie sich keine Sorgen, wenn Ihnen das Wort "Interactors" fremd vorkommt, wir werden es bald erfahren.

Von innen nach außen werden wir jede der Schichten etwas weiter erforschen. Wir verwenden eine uns vertraute Beispielanwendung: Zähler. Das Verständnis nimmt keine Zeit in Anspruch, daher können wir uns auf das Thema dieses Artikels konzentrieren.

Eine Demo der App finden Sie hier. Die Codebeispiele befinden sich in TypeScript. Einige der folgenden Code-Gists verwenden React und Redux. Etwas Wissen über diese Lösungen kann zum Verständnis beitragen. Die Konzepte von Clean Architecture sind jedoch viel universeller. Sie werden es auch ohne Vorkenntnisse der genannten Tools verstehen können.

Entitäten

Entitäten werden im Diagramm als Unternehmensgeschäftsregeln angezeigt. Entitäten umfassen Geschäftsregeln, die für ein Unternehmen universell sind. Sie repräsentieren Unternehmen, die für ihren Tätigkeitsbereich grundlegend sind. Sie sind die Komponenten mit dem höchsten Abstraktionsgrad.

In unserem Counter-Beispiel gibt es eine sehr offensichtliche Entität: den Counter selbst.

Anwendungsfälle

Anwendungsfälle werden als Anwendungsgeschäftsregeln bezeichnet. Sie stellen jeden Anwendungsfall einer einzelnen Anwendung dar. Jedes Element dieser Ebene stellt eine Schnittstelle zur äußeren Ebene bereit und fungiert als Hub, der mit anderen Teilen des Systems kommuniziert. Sie sind für die vollständige Ausführung der Anwendungsfälle verantwortlich und werden im Allgemeinen als Interaktoren bezeichnet.

In unserem Beispiel haben wir einen Anwendungsfall zum Inkrementieren oder Dekrementieren unseres Zählers:

Beachten Sie, dass die Factory-Funktion für ChangeCounterInteractor einen Parameter vom Typ CounterGateway empfängt. Wir werden die Existenz dieses Typs später in diesem Artikel besprechen. Aber wir können sagen, dass Gateways zwischen Use Cases und der nächsten Ebene stehen.

Schnittstellenadapter

Diese Ebene besteht aus der Grenze zwischen den Geschäftsregeln des Systems und den Tools, mit denen es mit der Außenwelt interagieren kann, z. B. Datenbanken und grafische Oberflächen. Elemente in dieser Ebene fungieren als Vermittler, die Daten von einer Ebene empfangen und an die andere weiterleiten und die Daten nach Bedarf anpassen.

In unserem Beispiel haben wir mehrere Schnittstellenadapter. Eine davon ist die React-Komponente, die den Zähler und seine Steuerelemente zum Inkrementieren und Dekrementieren darstellt:

Beachten Sie, dass die Komponente keine Counter-Instanz verwendet, um ihren Wert darzustellen, sondern stattdessen eine CounterViewData-Instanz. Wir haben diese Änderung vorgenommen, um die Darstellungslogik von den Geschäftsdaten zu entkoppeln. Ein Beispiel hierfür ist die Logik der Ausstellung des Zählers basierend auf dem Anzeigemodus (römische oder hindu-arabische Ziffern). Eine Implementierung von CounterViewData folgt unten:

Ein weiteres Beispiel für einen Schnittstellenadapter wäre die Redux-Implementierung unserer Anwendung. Module, die für Anforderungen an einen Server und die Verwendung von lokalem Speicher verantwortlich sind, würden ebenfalls in dieser Schicht leben.

Frameworks und Treiber

Die Werkzeuge, mit denen Ihr System mit der Außenwelt kommuniziert, bilden die äußerste Schicht. In dieser Ebene schreiben wir normalerweise keinen Code, der Bibliotheken wie React / Redux, Browser-APIs usw. enthält.

Die Abhängigkeitsregel

Diese Unterteilung in Schichten hat zwei Hauptziele. Eine davon ist, die Verantwortlichkeiten jedes Teils des Systems zu verdeutlichen. Zum anderen soll sichergestellt werden, dass jeder von ihnen seine Rolle so unabhängig wie möglich ausfüllt. Dazu gibt es eine Regel, in der festgelegt ist, wie die Elemente voneinander abhängen sollen:

Ein Element darf nicht von einem Element abhängig sein, das zu einer anderen Ebene gehört.

Beispielsweise kann ein Element in der Ebene "Use Cases" keine Informationen zu Klassen oder Modulen haben, die sich auf die grafische Benutzeroberfläche oder die Datenpersistenz beziehen. Ebenso kann eine Entität nicht wissen, welche Use Cases davon Gebrauch machen.

Diese Regel hat möglicherweise Fragen in Ihrem Kopf aufgeworfen. Nehmen Sie zum Beispiel einen Anwendungsfall. Es wird als Ergebnis der Benutzerinteraktion mit der Benutzeroberfläche ausgelöst. Seine Ausführung beinhaltet die Aktualisierung in einem dauerhaften Datenspeicher wie einer Datenbank. Wie kann der Interactor die relevanten Aufrufe an die Aktualisierungsroutinen vornehmen, ohne von einem Schnittstellenadapter abhängig zu sein, der für die Datenpersistenz verantwortlich ist?

Die Antwort liegt in einem Element, das wir bereits erwähnt haben: Gateways. Sie sind dafür verantwortlich, die Schnittstelle einzurichten, die von den Anwendungsfällen für ihre Arbeit benötigt wird. Sobald sie diese Schnittstelle eingerichtet haben, müssen die Schnittstellenadapter ihre Vertragspartei erfüllen, wie in der obigen Abbildung dargestellt. Wir haben die CounterGateway-Schnittstelle und eine konkrete Implementierung mit Redux:

Sie brauchen es vielleicht nicht

Natürlich war diese Beispielanwendung für eine Inkrement / Dekrement-Zähler-App etwas zu kompliziert. Und ich möchte klarstellen, dass Sie all dies nicht für ein kleines Projekt oder einen Prototyp benötigen. Vertrauen Sie mir, wenn Ihre Anwendung größer wird, möchten Sie die Wiederverwendbarkeit und Wartbarkeit maximieren. Eine gute Software-Architektur macht Projekte zeitbeständig.

Okay ... na und?

In diesem Artikel haben wir einen Ansatz zur Entkopplung der Entitäten unserer Systeme gefunden. Dies erleichtert die Wartung und Erweiterung. Um beispielsweise dieselbe Anwendung mit Vue.js zu erstellen, müssten nur die Komponenten CounterPage und CounterWidget neu geschrieben werden. Der Quellcode der Beispielanwendung befindet sich unter dem folgenden Link:

Diese Geschichte wurde von mir ins Portugiesische übersetzt! Es ist hier erhältlich.

Welche Vor- und Nachteile sehen Sie in diesem Ansatz? Haben Sie in der Produktion etwas Ähnliches verwendet? Teilen Sie Ihre Erfahrungen in den Antworten. Wenn Ihnen der Artikel gefällt, klatschen Sie bitte für mich!