Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modulare Flutter Apps — oder: Wie erweitere ich meine App, ohne die App anzufassen?

Modulare Flutter Apps — oder: Wie erweitere ich meine App, ohne die App anzufassen?

Oft wird davon gesprochen, dass Mobile Apps reaktiv sein, und viele Qualitätseigenschaften wie Wartbarkeit und Testbarkeit aufweisen müssen. Unter die Wartbarkeit und Testbarkeit fällt noch die Modularität. Kleine, in sich geschlossene Module lassen sich einfacher testen als ein großes Modul. Und weniger Code ist in der Regel einfacher zu warten als viel Code. Im Backend gibt es als Antwort Microservices, im Frontend arbeitet man mit Microfrontends.

Wie sieht es bei Flutter aus? Diese Frage möchte ich in diesem Talk anhand eines handlichen Beispiels näher beleuchten und mit einem möglichen Lösungsweg beantworten.

Julian Finkler

November 15, 2023
Tweet

Other Decks in Programming

Transcript

  1. JULIAN FINKLER - Vollzeit Angestellt bei billbee.io (Angular, .NET) -

    Sidehustle: mintware.de (Flutter, Dart, PHP…) - Flutter Anwender seit 2018 (v0.x.x) - iSAQB CPSA-F SENIOR SOFTWARE DEVELOPER IRGENDWAS MIT SOFTWARE ARCHITEKTUR @DEVTRONIC + @MINTWARE-DE /IN/JULIAN-FINKLER/ MINTWARE.DE
  2. Wiederverwendbarkeit Teile einer App können in anderen Apps wiederverwendet werden.

    Verbesserung der Wartbarkeit Durch die Reduzierung auf das wesentliche und die klaren grenzen wird die Wartbarkeit verbessert. Klare Grenzen Jeder Bereich hat seine festgelegte Verantwortung / seinen festgelegten Fachbereich. (Ähnlich wie DDD) Multi-Team-Support Wenn eine App umfangreicher wird, macht es ggf. Sinn die Verantwortung der Teilbereiche in verschiedene Teams zu verteilen. Build Beschleunigung* Durch Build Caches kann der Build zukünftig beschleunigt werden. Kein manuelles instanziieren von Screens Mit Dependency Injection wollen wir die benötigten Widgets erhalten aber nicht selbst instanziieren. Flexibles Routing Die App selber sollte keine Routen kennen. Ziele / Erwartungen
  3. Schnellstart Man investiert nicht so viel Zeit in die Architektur

    und kann schnell eine App veröffentlichen. (Verständlich*) Bekannte Muster, bspw. für die Ordnerstruktur, Code liegt nicht „verteilt“ Direkte Abhängigkeiten Jede Änderung in einem Teil des Codes kann alles beeinträchtigen. Konkrete Implementierungen verwenden Man wird nicht gezwungen Abstraktionen zu verwenden, weswegen gerne eine konkrete Implementierung verwendet wird. Team Split Es ist schwieriger in großen oder verteilten Teams an der gleichen Code Basis Änderungen herbeizuführen. Unvorhersehbarkeit Man kann nicht vorhersehen, wer welchen Teil der App wo (zukünftig) verwendet.
  4. ZUMINDEST SOLLTEN SIE KEINE SEIN* APPS SIND KEINE MONOLITHEN +

    Einzelne Funktionen einer App können dank pub schon in eigene Pakete ausgelagert werden. - Probleme nach wie vor: Erweiterbarkeit erhalten / OCP konsequent befolgen. - Oft werden Abhängigkeiten keine Beachtung geschenkt.
  5. Branch: 2-modulith Wer (Baustein) darf/muss mit wem sprechen? Wer (Dev)

    darf welchen Code verändern? 1. GRENZEN SCHAFFEN https://github.com/devtronic/modulare-flutter-apps
  6. BESTELLUNGEN, PRODUKTE, KONTO… AUFTEILUNG IN FEATURE-PAKETE + Abhängigkeiten in Feature

    Paketen werden reduziert + Feature Pakete können in verschiedenen Teams entwickelt werden. - App hängt weiterhin direkt von den Feature-Paketen ab. - Manuelles managen von Abhängigkeiten!
  7. BESTELLUNGEN, PRODUKTE, KONTO… AUFTEILUNG IN FEATURE-PAKETE + Abhängigkeiten in Feature

    Paketen werden reduziert + Feature Pakete können in verschiedenen Teams entwickelt werden. - App hängt weiterhin direkt von den Feature-Paketen ab. - Manuelles managen von Abhängigkeiten! Tasks Time Tracking Reporting …
  8. Klare Grenzen Durch das verschieben der Logik in separate Pakete

    gibt es nun klare Grenzen. Es kann gesteuert werden, worauf andere Zugriff haben und worauf nicht. Code Umfang Die Menge an Code in der „Haupt-App“ ist drastisch gesunken. Teams Split Es ist nun möglich, dass unterschiedliche Teams mit unterschiedlichen Repositories die Pakete entwickeln. (Git Submodule, Push-Regeln etc. können genutzt werden) Direkte Abhängigkeiten App hängt nach wie vor zu Stark von den Feature Paketen ab. Die Hauptnavigation wird weiterhin von der App aufgebaut Dependency Management Die App muss nach wie vor die konkreten Implementierungen (Repositories) erzeugen. pubspec.yaml Ein neuer Trade-Off: Pakete müssen nun in der pubspec.yaml hinterlegt werden.
  9. Branch: 3-move-routes-to-feature-packages Wie kann ich meine App erweitern, ohne die

    App selber anzufassen? Teil I 2. EXTENSION POINTS https://github.com/devtronic/modulare-flutter-apps
  10. BETRACHTET EURE APP ALS FRAMEWORK PLUGIN ANSATZ + Bietet Extension

    Points + Greift auf das Registry Pattern zurück + Verwendet Hooks / Event Dispatching + Hängt von Interfaces ab + Details können ignoriert werden + Abhängigkeiten müssen nicht mehr aktiv gemanaged werden. - Höherer Initialaufwand
  11. Generalisierung Routen werden nun generalisiert erzeugt / behandelt. Neue Module

    müssen somit nur noch als pubspec.yaml Referenz hinzugefügt und gebootstraped werden. App hängt stark von den Feature Paketen ab. Die Hauptnavigation wird weiterhin von der App aufgebaut. Es muss nun explizit eine Bootstrap Funktion aufgerufen werden. Direkte Abhängigkeiten
  12. Branch: 4-use-catalyst_builder Wie kann ich die Feature Pakete laden, ohne

    selber Code zu schreiben? (Teil II) Wie erzeuge ich Widgets, ohne sie selber zu instanziieren? 3. BOOTSTRAPPING LOSWERDEN https://github.com/devtronic/modulare-flutter-apps
  13. LÖSUNGEN get_it ist wohl das bekannteste Paket um das Problem

    (teilweise) zu lösen. - Services werden manuell registriert - Kein Autowiring - Keine „Parameter“ welche Injected werden können. - Services müssen weiterhin manuell verwaltet werden. - GetIt Instance ist static! GET_IT Es gibt noch viele weitere Pakete, welche ähnlich vorgehen: - flutter_modular - injector - …
  14. LÖSUNGEN build_runner ist ein offizielles Dart Paket und Tool, welches

    Code Änderungen überwacht und Aufgaben ausführt. BUILD_RUNNER -> Code Generierung
  15. CATALYST_ BUILDER Herausgewachsen aus catalyst (2018) und flutter_catalyst (2020). -

    Inspiriert von anderen Frameworks - Komplett neuer Ansatz - Code Generator - Paket wird zur Laufzeit nicht benötigt A LIGHTWEIGHT AND EASY TO USE DEPENDENCY INJECTION PROVIDER BUILDER FOR DART. Registrierung per Annotations Das komplette Paket arbeitet mit Annotations. Services werden direkt bei der Deklaration als „Service“ markiert. Auto Wiring Service A benötigt Service B? Kein Problem, markiere beides mit @Service und der Container kümmert sich um den Rest 😉 Taggable Services Du hast mehrere Services welche oft zusammen benötigt werden? Tagge sie und du kannst sie als Liste injecten.
  16. Wartbarkeit Service Abhängigkeiten müssen nicht mehr aktiv gemanagt werden. Erweiterbarkeit

    Neue Funktionen erfordern in der Regel keinen Eingriff in die bestehende App. Obsolete Module können idealerweise einfach gelöscht werden. Datenabhängigkeit Die Datenabhängigkeit (Tasks <-> Time Tracking) bleibt nach wie vor bestehen.
  17. KEINE 1:1 KOPIE, JEDER FACHBEREICH IST UNTERSCHIEDLICH! DATEN DUPLIZIEREN +

    Verwendet einen Event Dispatcher, Message Bus o.ä. + Jeder Fachbereich nimmt die Informationen aus dem Event, die er braucht und legt sie für sich ab. + Für die eigene Funktion werden die anderen Bereiche i.d.R. nicht mehr benötigt. - Doppelte Daten sind aufwändiger aktuell zu halten - Partial Persistence / Fehlerbehandlung ist aufwändig
  18. EVENT_DISPATCHER_ BUILDER Datenaustausch unter Modulen mit Events A EVENT DISPATCHER

    BUILDER FOR DART. EASY TO USE AND LIGHTWEIGHT AS A FEATHER. Unterstützung der Modularisierung Keine direkte Datenabhängigkeiten mehr zwischen den Paketen. Jeder Subscriber nimmt sich die Daten, die er benötigt. Leichtgewichtig In Kombination mit catalyst_builder lässt sich einfach App übergreifend ein Datenaustausch umsetzen. Einfach zu verwenden Man benötigt für das Daily Business kein Handbuch.
  19. Entkopplung von Daten Separate Datenhaltung entkoppelt die Module noch weiter

    voneinander. Fehler Resilienz Wenn voneinander abhängige Module teilweise nicht geladen wurden, können im schlimmsten Fall nur die Bereiche nicht sinnvoll genutzt werden. Es findet aber kein Fehler auf Programmebene statt. pubspec.yaml Pakete müssen nur noch in der pubspec.yaml verwaltet werden.
  20. Zusammenfassung 1. Grenzen schaffen • Fachbereiche identifizieren und als pub-Pakete

    extrahieren • Geteilte Bausteine identifizieren, abstrahieren und ein „shared“ pub-Paket extrahieren 2. Extension-Points integrieren • Stellen identifizieren, welche sich innerhalb der App in Abhängigkeit der Fachbereiche ändern (Navigation, Routing etc.) • Bewährte Patterns (Registry, Factory, etc.) anwenden um diese Kopplungen aufzuweichen • Trennt das, was gleich bleibt, von dem, was sich verändert.
  21. Zusammenfassung 3. Bootstrapping loswerden • Verwendet Code Generatoren für regelmäßige

    / Aufgaben gleicher Natur. • App: Nicht automatisch generierter Code darf keine direkten Verweise auf Feature Pakete haben (Imports, “new“, …) 4. Weiter entkoppeln • Prüft, wer welche Daten benötigt. (Sehr explizit auf Feldebene) • Verwendet Event Dispatching, Messaging o.Ä. um eine Kommunikation zwischen den Features zu ermöglichen • Wenn nötig: Pro Feature Paket eine eigene Datenhaltung der benötigten Daten anderer Feature Pakete
  22. Q&A