Dies ist der erste Beitrag in einer Reihe von Veröffentlichungen, in denen mein Verständnis der Anwendungsarchitektur von Flutter erläutert wird. Ich warne Sie - es wird sehr selbstbewusst sein .
Bisher geplant:
Vorwort
Ich bin seit ungefähr 20 Jahren in der Programmierung. Ich habe vor 4 Jahren mit Xamarin.Forms mit der mobilen Entwicklung begonnen, da plattformübergreifend die einzige Motivation für mich als Indie-Entwickler war. Xamarin.Forms fordert Sie buchstäblich dazu auf, das MVVM-Muster zu verwenden, da die Benutzeroberfläche in XAML definiert ist und Sie eine Art Ebene benötigen, um die Benutzeroberfläche mit dem Modell zu verkleben. Während der Arbeit mit Xamarin traf ich ReactiveUI und war buchstäblich fasziniert von den Streams und reaktiven Erweiterungen ( Rx ), die meine Anwendungen zuverlässiger machten.
Während Xamarin.Forms MVVMs sofort einsatzbereit waren, war ich beim Wechsel zu Flutter überrascht, dass es keine ähnlichen Designmuster gab. Ich begann, die verschiedenen vorgeschlagenen Ansätze zu untersuchen, aber keiner der verfügbaren stellte mich vollständig zufrieden:
- InheritedWidget : Ich konnte nicht nur den geänderten Teil des Widget-Baums aktualisieren, daher habe ich ihn nur für den Zugriff auf die Modellklassen verwendet, in denen Dart-Streams (Dart-Streams) veröffentlicht wurden. Diese Idee wurde jedoch bald zugunsten der Vorlage Service Locator aufgegeben
- Das Scoped-Modell ist interessanter als das
InheritedWidget
, aber es hat mir nicht so viel Flexibilität gegeben, wie ich es von ReactiveUI gewohnt war - Redux war die Vorlage, die von vielen Entwicklern empfohlen wurde, die mit React Native vertraut sind. Ich habe einen ganzen Beitrag darüber, warum ich es nicht mag.
- BLoC : Wenn ich nicht bereits zu einem Zeitpunkt mit der Entwicklung meines eigenen Musters begonnen hätte, als BLoC Fortschritte machte, hätte ich es höchstwahrscheinlich genommen, da es wirklich flexibel und reaktiv ist. Was mir nicht gefällt, ist, dass es Stream Sinks veröffentlicht und ich nicht einfach Funktionen oder Befehle an den Ereignishandler des Widgets übergeben kann. Darüber hinaus sagt Ihnen BLoC weder, wie Sie Ihre Anwendung als Ganzes strukturieren sollen, noch gibt es eine klare Definition, wie groß ein bestimmtes BLoC sein sollte oder welchen Umfang es hat
- MVVM : Da ich mit ihm zusammengearbeitet habe, war dies das erste, was ich in Flutter implementieren wollte. Aber nein! Der Zweck von ViewModel besteht darin, die Darstellung Ihres Modells in View durch Bindungen ordnungsgemäß bereitzustellen. Flutter aktualisiert seine Modelle jedoch nicht mit neuen Daten, sondern erstellt sie immer neu, wie ich bereits beschrieben habe . Darüber hinaus sollten ViewModels immer mit dem Basismodell synchronisiert werden, was zu unangenehmen Fehlern führt. Die Realität zeigt, dass der versprochene Vorteil der Wiederverwendung von ViewModels in Anwendungen fast nie erreicht wird. Adam Pedley hat einen großartigen Beitrag über diese Mängel
Die harte Wahrheit über Schichtredundanz
Es ist fast ein Dogma für die Idee, dass Sie Ihre Anwendung in der Entwicklung immer in mehreren Ebenen erstellen sollten, von denen jede nur Zugriff auf den Basiswert hat, da dies Ihnen Folgendes ermöglicht:
- Ebenen in anderen Projekten wiederverwenden
- Ersetzen Sie transparent eine Schicht durch eine andere
- Testen vereinfachen
Allerdings:
- In meiner Praxis gab es keinen Fall, in dem ich eine vollständige Wiederverwendung von Schichten beobachtete. Wenn Sie einen universellen Code haben, der wiederverwendet werden kann, ist es sinnvoller, ihn in eine Art universelle Bibliothek zu stellen.
- Das Ersetzen ganzer Schichten ist ebenfalls nicht üblich. Es ist unwahrscheinlich, dass die meisten Benutzer die Datenbank ersetzen, nachdem die Anwendung einen bestimmten Entwicklungsstand erreicht hat. Warum also eine Abstraktionsschicht hinzufügen? Wenn einige unserer aktuellen Entwicklungstools benötigt werden, ist das Refactoring recht einfach.
- Was wirklich funktioniert, ist die Vereinfachung des Testens
Ich bin jedoch nicht gegen die Verwendung von Schichten, sollten wir diese Regel so rücksichtslos wie zuvor befolgen. Eine übermäßige Verwendung führt zu einer Zunahme des Codes und kann möglicherweise zu Problemen führen, während eine einzige Quelle für den Anwendungsstatus beibehalten wird. Wenden Sie daher Ebenen an, wenn dies wirklich erforderlich ist und nicht auf "Best Practices" basiert.
Ideale Architektur für Flutter
Was erwarte ich von einer perfekten Architektur?
- Einfaches Verständnis der Anwendung. Für mich ist dies das wichtigste Ziel. Neue Entwickler, die an der Arbeit mit vorhandenem Code beteiligt sind, sollten die Entwicklungsstruktur leicht verstehen
- Teamentwicklung einfach
- Die Architektur selbst sollte leicht zu verstehen und zu warten sein.
- Kein Vorlagencode für Krücken in der Entwicklung
- Unterstützung für flatterreaktiven Stil
- Einfaches Debuggen
- Leistung sollte nicht leiden
- Einfache Erweiterung
- Einfache Prüfung
- Möglichkeiten, sich auf die Anwendung zu konzentrieren, anstatt in der Quelle zu wandern
Widget-Unabhängigkeit
Aufgrund der Art zustandsloser Schnittstellenelemente sollte keine Seite / kein Widget in Flutter von anderen abhängen oder diese beeinflussen. Dies führt zu der Idee, dass jede Seite / jedes Widget unabhängig dafür verantwortlich sein sollte, sich selbst und alle seine Interaktionen mit dem Benutzer anzuzeigen.
Rxvms
RxVMS ist eine Weiterentwicklung des in einem früheren Beitrag beschriebenen RxVAMS-Musters, bei dessen praktischer Anwendung einige Probleme identifiziert und behoben wurden.
Das aktuelle Ergebnis all dieser Gedanken ist das RxVMS- oder Rx-View-Managers-Services-Muster. Es führt alle oben genannten Aufgaben mit der einzigen Anforderung aus, dass Sie Rx-Threads und -Elemente verstehen müssen. Um Ihnen dabei zu helfen, widme ich den folgenden Beitrag.
Hier ist ein kurzer Überblick über meine Bewerbung

Dienstleistungen
Dies sind Abstraktionen von Schnittstellen zur Außenwelt, die als Datenbanken, REST-APIs usw. dienen können. Sie haben keinen Einfluss auf den Status der Anwendung.
Manager
Manager gruppieren semantisch ähnliche Funktionen wie Authentifizierung, Bestellverfahren und dergleichen. Manager manipulieren den Status eines Objekts.
Jede Statusänderung (Änderungen der Anwendungsdaten) sollte nur durch Manager vorgenommen werden. In der Regel speichern Manager selbst keine Daten, außer in Fällen, die für die Leistung oder Laufzeitkonstanten kritisch sind.
Manager können als Proxy-Datenquellen dienen, falls ihre bestimmte Transformation nach einer Anforderung von Diensten erforderlich ist. Ein Beispiel ist das Kombinieren von Daten aus zwei oder mehr Quellen zur Anzeige in einer Ansicht.
Manager können miteinander interagieren.
Ansichten
In der Regel ist dies StatefullWidget oder StreamBuilder, die Daten von Managern und Diensten verwenden können. Es kann eine ganze Seite oder ein Widget sein. Ansichten speichern keinen Status und können sich direkt an Dienste wenden, während diese Regel eingehalten wird.
Domänenobjekte
Obwohl nicht im Diagramm enthalten, sind dies wichtige Einheiten, die das Geschäftsmodell darstellen. In anderen Mustern können sie in Verbindung mit der Geschäftslogik zu einer separaten Schicht ( Geschäftsmodell ) gehören. Hier in RxVMS enthalten sie keine Logik, die den Status der Anwendung ändert. Fast immer handelt es sich um einfache Datentypen - einfache Datenobjekte (wenn ich sie in das Muster aufgenommen hätte, würde es wie RxVMMS aussehen, was etwas lang ist und zu Verwirrung führt - die VM könnte als ViewModel verwechselt werden). Logischerweise befinden sich Domänenobjekte in der Managerschicht.
Die folgenden Beiträge zeigen die Essenz und Interaktion dieser Elemente.