Architekturprobleme in großen Projekten

Die Entwicklung mobiler Apps scheint eine ziemlich einfache Aufgabe zu sein. Es scheint, was dort zu tun ist? Ich warf ein paar Ansichten, salbte es mit etwas Architektur, und das war es, das Projekt ist fertig, Sie können die Anwendung an die Station senden. In einer Reihe von Artikeln werde ich auf die Funktionen eingehen, auf die wir bei der Entwicklung einer Anwendung für eine große Bank gestoßen sind.


Betrachten Sie 5 wichtige Themen. Natürlich wurden die meisten von ihnen mehrmals in der Community diskutiert, aber hinter jedem Thema stehen Schmerzen, Tränen, Zeitverlust und vor allem die Erfahrung, die uns nützlich war, und ich hoffe, dass es Ihnen nützlich sein wird.


Bild


Zu Beginn der Entwicklung einer mobilen Anwendung stellt sich dem Marktführer oder Designer die Frage, welches Architekturmuster zu verwenden ist. Unser Studio hat ein gemeinsames Architekturmuster MVP. Pure MVP ist in seiner reinen Form sicherlich gut (siehe Bild unten), aber wir wären keine echten Entwickler, wenn wir dieses Muster nicht finalisieren würden. Sie haben nicht bei einer Option Halt gemacht, und wir haben zwei Zweige von reinem MVP erhalten.


Bild


Daher standen wir in der Entwurfsphase vor der Aufgabe, eines auszuwählen und auf der Grundlage eines allgemein akzeptierten und verständlichen Architekturmusters fortzufahren. Aber schon in einem so frühen Stadium ist es uns gelungen, einen Fehler zu machen, der uns später viele Probleme bescherte.


Schauen wir uns zwei unserer MVPs zu Steroiden an.


SurfMVP


Bild


Das Bild zeigt, dass sich im Vergleich zum üblichen MVP nicht viel geändert hat. Beim Wechseln zwischen Bildschirmen in iOS-Anwendungen sind einige Probleme aufgetreten. Eine große Menge an Logik für die Bildung neuer Bildschirme, bevor der Übergang direkt im UIViewController konzentriert wird, erschien uns nicht ganz richtig. Deshalb haben wir zuerst die Router-Entität getrennt, die für die Übergänge zwischen Bildschirmen in der Anwendung verantwortlich ist.
Modelle in SurfMVP sind die Dienste, die Presenter zum Abrufen von Daten aufruft. Oft löst ein Dienst die Aufgaben für das gesamte Modul, aber in schwierigen Situationen muss man mit mehreren interagieren.
Die Configurator-Entität ist für die Erstellung eines separaten Moduls verantwortlich, initialisiert alle erforderlichen Komponenten und ist für die Erstellung von Abhängigkeiten zwischen ihnen verantwortlich.


Bild


Das Hauptmerkmal von SurfMVP ist, dass jede Schicht in MVP durch ein Protokoll getrennt ist. Das Bild zeigt ein Diagramm der Schichten und die Beziehung der Protokolle zwischen ihnen. Protokolle werden benötigt, damit jede Schicht von der anderen getrennt und theoretisch leicht ersetzt werden kann. Jede der Schichten sollte keine Implementierungsdetails offenlegen.


Betrachten wir sie getrennt:


ViewInput - implementiert View selbst, Presenter behält den Link bei. Dieses Protokoll beschreibt die Methoden, mit denen Presenter die Ansicht steuern, Daten übertragen, Status ändern usw. kann.


ViewOutput - implementiert Presenter , View enthält einen Link dazu. Das Protokoll beschreibt eine Reihe von Aktionen, die in der Ansicht und in den Lebenszyklusmethoden ausgeführt werden können, z. B. Ereignisse der Benutzerinteraktion mit dem Bildschirm.


RouterInput - implementiert den Router , und Presenter behält eine Verknüpfung zu ihm bei, da dies der einzige ist, der für die weitere Navigation in der Anwendung verantwortlich ist.


ModuleTransitionable - View ist implementiert, Router behält eine Verknüpfung dazu. Dies ist das einzige "grundlegende" Protokoll in SurfMVP . Es wird benötigt, um dem Router eine Reihe von Methoden für die Arbeit mit der Anwendungsnavigation bereitzustellen.


ModuleInput - Implementiert Presenter . Dieses Protokoll muss Methoden enthalten, mit denen ein anderes Modul, das eine Verknüpfung zu diesem Protokoll enthält, den Status des aktuellen Moduls ändern kann.


ModuleOutput - implementiert den Presenter des aufrufenden Moduls, der Link enthält den Presenter des aufgerufenen Moduls. Wenn der Profilbildschirm über das Nachrichtenmodul angezeigt werden kann, sollte NewsPresenter ProfileModuleOutput implementieren und ProfilePresenter sollte einen Link dazu enthalten.
ModuleOutput wird an den Configurator des aufgerufenen Moduls übergeben und dort in Presenter installiert. Enthält Modulmethoden, die das Verhalten des aufrufenden Moduls beeinflussen.


SurfMVP Problem


Aus all diesen Gründen gibt es ein Hauptproblem: die Navigation. Wie sich herausstellte, bestand die Meldung zum Hervorheben einer separaten Router-Entität zwar aus Navigationsproblemen, diese waren jedoch nicht mehr vorhanden. SurfMVP wurde erfolgreich in Projekten mit einfacher flacher Navigation ohne komplexe DeepLinks und Push-Benachrichtigungen eingesetzt.


Das folgende Bild zeigt schematisch die Navigation in der Anwendung mit SurfMVP. Jedes einzelne Modul kommuniziert über einen eigenen Router mit dem anderen. Auf diese Weise wird die Navigation eines beliebigen Flusses in der Anwendung erstellt.


Bild


Diese Art der Navigation eignet sich gut für den Fall, dass der Benutzer den Ablauf seiner Wahl in der Anwendung durchläuft. Zum Beispiel in der folgenden Abbildung: Der Benutzer durchläuft die Bildschirme von Punkt A bis Punkt D, sodass er selbst einen Stapel erstellt, auf dem er auf die gleiche Weise vor- und zurückgehen muss.


Bild


Probleme beginnen in dem Moment, in dem der Benutzer von Punkt A nach Punkt D versetzt werden muss, und dies sollte ohne seine Teilnahme geschehen. Wenn ein Benutzer beispielsweise auf Push-Benachrichtigung klickt oder einem Link aus einer Drittanbieteranwendung folgt. Im Fall von SurfMVP müssen wir einen globalen Router hinzufügen, der die Navigation steuert, unabhängig davon, wo sich der Benutzer gerade in der Anwendung befindet. Um dieses Problem global zu lösen, haben wir uns entschlossen, die Koordinatoren zu nutzen. Gehen wir zu ihnen über.


Bild


Koordiniertes SurfMVP


Bild


Coordinated SurfMVP ist ein Architekturmuster, bei dem wir im Gegensatz zu SurfMVP die Router-Entität, die sich in jedem einzelnen Modul befand, entfernt haben. Das Paradigma, eine Anwendung zu erstellen, hat sich ein wenig geändert. Module sind nicht mehr vollständig unabhängig. Jedes Modul, mit Ausnahme von vollständig wiederverwendbaren Modulen, befindet sich in einem separaten UserFlow, der wie geplant einige allgemeine Aktionen ausführen sollte, die den Benutzer zum gewünschten Ergebnis führen.


Ein Beispiel für einen solchen Fluss in unserer Anwendung ist der Zahlungsfluss. Zahlungen sind eine Reihe von Bildschirmen, auf denen der Benutzer eine Überweisung oder Zahlung auf verschiedene Arten vornehmen kann.


In Coordinated SurfMVP ersetzte die Router-Entität die Coordinator- Entität, die jetzt nicht nur für die Navigation eines einzelnen Moduls, sondern für eine Reihe von Modulen zuständig ist, die logisch miteinander verbunden sind. Dies vereinfacht die Navigation und das Arbeiten mit der Anwendung. Schematisch sieht unsere Bewerbung so aus:


Bild


Ganz oben befindet sich der ApplicationCoordinator, der für das anfängliche Routing in der Anwendung verantwortlich ist. In einem Fall, in dem der Benutzer autorisiert ist, werden wir ihn sofort an den Hauptteil der Anwendung senden. Andernfalls werden wir ihn an den Autorisierungsbildschirm senden.


Wenn unsere Anwendung Deeplinks oder Push-Notifications enthält, können wir jederzeit Initialisierungs- und Startregeln für Koordinatoren festlegen, damit diese den Stapel direkt an dem gewünschten Punkt D erstellen, über den wir zuvor gesprochen haben.


Bild


Schematisch sieht unsere Navigation jetzt so aus. Jeder einzelne UserFlow verweist auf seinen eigenen Koordinator, der seinerseits entscheidet, was in Zukunft passieren wird. Die Verantwortung für die Datenübertragung und die Einleitung der weiteren Navigation liegt jetzt beim Koordinator. Er ist bereits mit anderen Modulen oder anderen Koordinatoren verbunden, um den Navigationsstapel weiter aufzubauen.


Vor- und Nachteile von Coordinated SurfMVP


Vorteile:


  1. Der Hauptvorteil des Koordinatoransatzes besteht in der Möglichkeit, ganze Navigationsblöcke innerhalb der Anwendung wiederzuverwenden. Jetzt können Sie von überall in der Anwendung aus diesen Koordinator anrufen und über nichts anderes nachdenken, als seine Arbeit abzuschließen.
  2. Da die Navigationslogik in einem separaten Koordinator isoliert ist, ist es jetzt viel bequemer, der Navigation zu folgen: Öffnen Sie nur eine Datei und das gesamte Bild vor Ihren Augen. Es ist nicht mehr erforderlich, alle einzelnen Module zu durchstechen, um zu verstehen, worauf sie abzielen, die Anwendung zusammenzustellen und das Design zu betrachten.
  3. Es ist bequemer, in großen Teams zu entwerfen. In der Entwurfsphase eines separaten neuen Features ist es ausreichend, Zeit für den Aufbau der gesamten Navigation und die Initialisierung aller Module bereitzustellen. Anschließend wird die Entwicklung an eine große Anzahl von Entwicklern delegiert, und die Integration dieser Bildschirme untereinander wird weitaus problemloser.
  4. Die Integration von Deeplinks und Push-Notifications bereitet keine Kopfschmerzen mehr.

Nachteile:


Wie bei jedem architektonischen Ansatz gibt es auch bei Coordinated SurfMVP Nachteile.


  1. Große Koordinatoren taten weh. Aufgrund der Konzentration der gesamten Logik an einem Ort wird es viel schwieriger, nicht in einer großen Anzahl von Codezeilen zu ertrinken. Wenn Sie nicht dem Prinzip der geteilten Verantwortung folgen, kann der Koordinator natürlich zu einem großen Monster heranwachsen, und alle Vorteile der Codelesbarkeit verschwinden leicht.
  2. Sie müssen viel schreiben, um den Code schön zu machen. Aufgrund der großen Anzahl von Ebenen in der Anwendung, von denen jede für eine separate Aktion verantwortlich ist, müssen Sie diese Ebenen durchbrechen, um den gewünschten Koordinator zu erreichen.
  3. Speicherlecks - das Problem ist nicht neu, aber Sie sollten diese Angelegenheit befolgen, um nicht in das Loch zu geraten. Der Hauptgrund für Speicherverluste bei der Arbeit mit Koordinatoren ist das Beibehalten von Zyklen in Modulrückrufen. Daher müssen Sie die starken Verbindungen in den Verschlüssen sorgfältig überwachen.

Typischer Fall

Ein typischer Fall ist die Initialisierung des neuen Koordinators und die Implementierung von Closure FinishFlow. Die Erfassung eines weak coordinator ist obligatorisch, andernfalls verweist der Koordinator auf sich selbst, was zu einem Leck in Form von AuthCoordinator führt.


  func runAuthFlow() { let coordinator = AuthCoordinator(router: MainRouter()) coordinator.finishFlow = { [weak self, weak coordinator] in self?.removeDependency(coordinator) } self.addDependency(coordinator) coordinator.start() } 

Schlussfolgerungen


In der Entwurfsphase haben wir die Komplexität des Projekts unterschätzt und den falschen architektonischen Ansatz gewählt. Dieser Fehler hat jedoch dazu beigetragen, ein Regelwerk zu bilden und die Auswahl der Architektur bei der Initialisierung von Projekten sorgfältiger zu treffen.


Wann soll Coordinated SurfMVP verwendet werden?


In der Tat, wenn Sie wollen, dann verwenden Sie es, aber wir halten uns an die folgenden Bedingungen:


  • Die Struktur der Bildschirme ist komplex und kann sich ändern.
  • Es gibt Deeplinks und / oder Push-Notifications mit Navigation;
  • Es ist notwendig, in einem großen Team zu arbeiten.

Wann ist SurfMVP anzuwenden?


Wir haben unser erstes Architekturmuster nicht vergessen. Wir verwenden es immer noch im Studio, wenn wir Projekte entwickeln, wenn es die folgenden Bedingungen erfüllt:


  • Das Projekt ist klein genug und plant keine rasche Entwicklung.
  • Das Projekt hat eine sehr einfache Bildschirmstruktur und unterliegt keinen starken Änderungen.

Zusätzliche Materialien



In diesem Artikel habe ich ein Problem mit der Architektur beschrieben, auf die wir bei der Arbeit gestoßen sind. Die Wahl der zu verwendenden Architektur liegt natürlich bei Ihnen. Im nächsten Artikel werde ich die Probleme des Backends in großen Projekten erläutern und erläutern, wie wir sie gelöst haben. Bleib dran!

Source: https://habr.com/ru/post/de485172/


All Articles