Ceci est le premier article d'une série de publications expliquant ma compréhension de l'architecture d'application de Flutter. Je vous préviens - ce sera très sûr de vous .
Jusqu'à présent prévu:
Préface
Je suis en programmation depuis environ 20 ans. J'ai commencé le développement mobile il y a 4 ans avec Xamarin.Forms, car la multiplateforme était la seule motivation pour moi en tant que développeur indépendant. Xamarin.Forms vous pousse littéralement à utiliser le modèle MVVM, car l'interface utilisateur est définie dans XAML, et vous avez besoin d'une sorte de couche pour coller l'interface utilisateur avec le modèle. Dans le processus de travail avec Xamarin, j'ai rencontré ReactiveUI et j'ai été littéralement captivé par les flux et les extensions réactives ( Rx ) qui ont rendu mes applications plus fiables.
Alors que les MVVM Xamarin.Forms étaient prêtes à l'emploi, lorsque je suis passé à Flutter, j'ai été surpris qu'il n'y ait pas de modèles de conception similaires. J'ai commencé à rechercher les différentes approches proposées, mais aucune des approches disponibles ne m'a complètement satisfait:
- InheritedWidget : Je ne pouvais pas mettre à jour uniquement la partie modifiée de l'arborescence des widgets, je ne l'ai donc utilisée que pour accéder aux classes de modèle publiant des flux de fléchettes (Dart Streams), mais j'ai rapidement abandonné cette idée au profit du modèle Service Locator
- Le modèle Scoped est plus intéressant que
InheritedWidget
, mais il ne m'a pas donné autant de flexibilité que je l'étais avec ReactiveUI - Redux était le modèle recommandé par de nombreux développeurs familiers avec React Native. J'ai un post entier sur pourquoi je ne l'aime pas.
- BLoC : si je n'avais pas déjà commencé à développer mon propre modèle à un moment où BLoC a commencé à progresser, je l'aurais probablement pris, car il est vraiment flexible et réactif. Ce que je n'aime pas, c'est qu'il publie des récepteurs de flux et je ne peux pas simplement prendre et transmettre des fonctions ou des commandes au gestionnaire d'événements du widget. De plus, BLoC ne vous dit pas comment structurer votre application dans son ensemble, ni de définition claire de la taille d'un certain BLoC ni de sa portée.
- MVVM : depuis que j'ai travaillé avec lui, c'est la première chose que j'espérais implémenter dans Flutter. Mais non! Le point de vue de ViewModel est de fournir gracieusement la représentation de votre modèle dans View via des liaisons. Mais Flutter ne met pas à jour ses modèles avec de nouvelles données, il les reconstruit toujours, comme je l'ai déjà décrit . De plus, les ViewModels devraient toujours être synchronisés avec le modèle de base, ce qui conduit à des bugs désagréables, et la réalité montre que l'avantage promis de la réutilisation des ViewModels dans les applications n'est presque jamais atteint. Adam Pedley a un excellent article sur ces défauts
La dure vérité sur la redondance des couches
C'est presque un dogme à l'idée qu'en développement, vous devez toujours construire votre application en plusieurs couches, dont chacune n'a accès qu'au sous-jacent, car cela vous permettra:
- réutiliser les couches dans d'autres projets
- remplacer de façon transparente une couche par une autre
- simplifier les tests
Cependant:
- dans ma pratique, il n'y a eu aucun cas où j'ai observé une réutilisation complète des couches. Si vous avez un code universel qui peut être réutilisé, il est plus logique de le mettre dans une sorte de bibliothèque universelle;
- le remplacement de couches entières n'est pas non plus une pratique courante. Il est peu probable que la plupart des gens remplacent la base de données une fois que l'application a atteint un certain stade de développement, alors pourquoi lui ajouter une couche d'abstraction. Eh bien, si certains de nos outils de développement actuels sont requis, le refactoring est assez facile;
- ce qui fonctionne vraiment, c'est la simplification des tests
Je ne suis pas contre l'utilisation de couches, cependant, si nous suivons cette règle aussi imprudemment qu'auparavant. Leur utilisation excessive entraîne une augmentation du code et peut potentiellement créer des problèmes tout en conservant une source unique d'état d'application. Par conséquent, appliquez des couches lorsque cela est vraiment nécessaire et non basé sur les "meilleures pratiques".
Architecture idéale pour Flutter
Alors qu'est-ce que j'attends d'une architecture parfaite?
- Facilité à comprendre l'application. Pour moi, c'est l'objectif le plus important. Les nouveaux développeurs impliqués dans l'utilisation du code existant devraient facilement comprendre la structure de développement
- Facilité de développement d'équipe
- L'architecture elle-même doit être facile à comprendre et à entretenir.
- Pas de code modèle pour les béquilles en développement
- Prise en charge du style réactif Flutter
- Débogage facile
- Les performances ne devraient pas souffrir
- Expansion facile
- Facilité de test
- Opportunités de se concentrer sur l'application au lieu de se promener dans la source
Indépendance du widget
En fonction de la nature des éléments d'interface sans état, aucune page / widget dans Flutter ne devrait dépendre ou influencer les autres. Cela conduit à l'idée que chaque page / widget devrait être indépendamment responsable de son affichage et de toutes ses interactions avec l'utilisateur.
Rxvms
RxVMS est une évolution du modèle RxVAMS décrit dans un article précédent , au cours de l'application pratique dont certains problèmes ont été identifiés et résolus.
Le résultat actuel de toutes ces réflexions est le modèle RxVMS, ou Rx-View-Managers-Services. Il exécute toutes les tâches ci-dessus avec la seule exigence que vous devez comprendre les threads et les éléments Rx. Pour vous aider, je dédie le post suivant.
Voici un bref aperçu de ma candidature

Les services
Ce sont des abstractions d'interfaces avec le monde extérieur, qui peuvent servir de bases de données, d'API REST, etc. Ils n'affectent pas l'état de l'application.
Managers
Les gestionnaires regroupent des fonctionnalités sémantiquement similaires, telles que l'authentification, les procédures de commande, etc. Les gestionnaires manipulent l'état d'un objet.
Tout changement d'état (changements dans les données d'application) ne doit être effectué que par le biais des gestionnaires. En règle générale, les gestionnaires eux-mêmes ne stockent pas de données, sauf dans les cas critiques pour les performances ou les constantes d'exécution.
Les gestionnaires peuvent servir de sources de données proxy, au cas où leur certaine transformation serait requise après une demande de services. Un exemple consiste à combiner des données de deux sources ou plus pour les afficher dans une vue.
Les gestionnaires peuvent interagir les uns avec les autres.
Vues
Il s'agit généralement de StatefullWidget ou StreamBuilder, qui est capable d'utiliser les données des gestionnaires et des services. Il peut s'agir d'une page entière ou d'un widget. Les vues ne stockent aucun état et peuvent contacter directement les services tant que cette règle est respectée.
Objets de domaine
Bien qu'ils ne soient pas inclus dans le diagramme, ce sont des entités importantes qui représentent le modèle commercial. Dans d'autres modèles, ils peuvent appartenir à une couche distincte ( modèle commercial ) conjointement avec la logique métier. Ici, dans RxVMS, ils ne contiennent aucune logique qui modifie l'état de l'application. Presque toujours, ce sont des types de données simples - des objets de données simples (si je les incluais dans le modèle, cela ressemblerait à RxVMMS, ce qui est un peu long et prête à confusion - la machine virtuelle pourrait être confondue avec un ViewModel). Logiquement, les objets de domaine sont situés dans la couche des gestionnaires.
Les articles suivants révèlent l'essence et l'interaction de ces éléments.