Problèmes d'architecture dans les grands projets

Le développement d'applications mobiles semble être une tâche assez simple. Il semblerait que faire là-bas? J'ai jeté quelques vues, je l'ai oint avec une architecture, et c'est tout, le projet est prêt, vous pouvez envoyer l'application à la station. Dans une série d'articles, je partagerai les fonctionnalités que nous avons rencontrées lors du développement d'une application pour une grande banque.


Considérez 5 sujets importants. Bien sûr, la plupart d'entre eux ont été discutés plus d'une fois dans la communauté, mais derrière chaque sujet se trouvent la douleur, les larmes, le temps perdu et, plus important encore, l'expérience qui nous a été utile, et j'espère qu'elle vous sera utile.


image


Au début du développement d'une application mobile, le leader ou le designer se pose la question - quel modèle architectural utiliser? Notre studio a un MVP de modèle architectural commun. Pure MVP est certainement bon dans sa forme pure (voir l'image ci-dessous), mais nous ne serions pas de vrais développeurs si nous n'avions pas finalisé ce modèle. Ils ne se sont pas arrêtés sur une seule option, et nous avons obtenu deux branches de MVP pur.


image


Par conséquent, au stade de la conception, nous avons été confrontés à la tâche d'en choisir un et, sur la base d'un modèle architectural généralement accepté et compréhensible, de continuer. Mais, déjà à un stade aussi précoce, nous avons réussi à commettre une erreur, ce qui nous a par la suite posé de nombreux problèmes.


Regardons deux de nos MVP sur les stéroïdes.


SurfMVP


image


L'image montre que, par rapport au MVP habituel, peu de choses ont changé. Nous avons remarqué des problèmes lors du basculement entre les écrans dans les applications iOS. Une grande partie de la logique pour la formation de nouveaux écrans avant la transition est concentrée directement dans l'UIViewController, cela ne nous a pas semblé tout à fait raison, donc la première chose que nous avons faite a été de séparer l'entité Router, qui est chargée de faire des transitions entre les écrans dans l'application.
Les modèles dans SurfMVP sont les services que Presenter invoque pour récupérer des données. Souvent, un service résout les tâches de l'ensemble du module, mais dans des situations difficiles, vous devez interagir avec plusieurs.
L'entité Configurateur est responsable de la construction d'un module distinct, elle initialise tous les composants nécessaires et est responsable de la création de dépendances entre eux.


image


La caractéristique principale de SurfMVP est que chaque couche dans MVP est séparée par un protocole. L'image montre un diagramme des couches et la relation des protocoles entre elles. Des protocoles sont nécessaires pour que chaque couche soit séparée de l'autre et en théorie soit facilement remplacée. Chacune des couches ne doit pas divulguer les détails d'implémentation.


Examinons-les séparément:


ViewInput - implémente View lui-même, Presenter conserve le lien. Ce protocole décrit les méthodes par lesquelles Presenter peut contrôler la vue , transférer des données, modifier des états, etc.


ViewOutput - implémente Presenter , View détient un lien vers celui-ci. Le protocole décrit un ensemble d'actions pouvant se produire dans la vue et les méthodes de cycle de vie, par exemple, les événements d'interaction de l'utilisateur avec l'écran.


RouterInput - implémente Router , et Presenter conserve un lien vers celui-ci, car il est le seul responsable de l'initiation de la navigation dans l'application.


ModuleTransitionable - La vue est implémentée, le routeur y maintient un lien. Il s'agit du seul protocole "de base" de SurfMVP . Il est nécessaire pour fournir au routeur un ensemble de méthodes pour travailler avec la navigation des applications.


ModuleInput - Implémente Presenter . Ce protocole doit contenir des méthodes par lesquelles un autre module qui détient un lien vers ce protocole pourrait changer l'état du module actuel.


ModuleOutput - implémente le présentateur du module appelant, le lien contient le présentateur du module appelé. Si l'écran de profil peut être affiché à partir du module d'actualités, alors NewsPresenter doit implémenter ProfileModuleOutput et ProfilePresenter doit contenir un lien vers celui-ci.
ModuleOutput est transmis au configurateur du module appelé et y est installé dans Presenter . Contient des méthodes de module qui affectent le comportement du module appelant.


Problème SurfMVP


Sur la base de tout ce qui précède, il y a un problème principal - la navigation. Bien que le message pour mettre en évidence une entité de routeur distincte était des problèmes de navigation, il s'est avéré qu'ils avaient disparu, mais pas pour longtemps. SurfMVP a été utilisé avec succès sur des projets avec une navigation simple à plat , sans DeepLinks et Push-Notifications complexes.


L'image ci-dessous montre schématiquement la navigation dans l'application avec SurfMVP. Chaque module individuel communique avec l'autre via son propre routeur. Ainsi, la navigation de tout flux dans l'application est construite.


image


Ce type de navigation est bien adapté au cas où l'utilisateur passe par le flux de son choix dans l'application. Par exemple, dans l'image ci-dessous: l'utilisateur parcourt les écrans du point A au point D, donc il construit lui-même une pile sur laquelle il doit aller et revenir de la même manière.


image


Les problèmes commencent au moment où il est nécessaire de transférer l'utilisateur du point A au point D, et cela devrait se produire sans sa participation. Par exemple, si un utilisateur clique sur Push-Notification ou suit un lien à partir d'une application tierce. Dans le cas de SurfMVP, nous devrons ajouter un routeur global qui contrôlera la navigation indépendamment de l'endroit où l'utilisateur se trouve actuellement dans l'application. Pour résoudre ce problème globalement, nous avons décidé d'utiliser les coordinateurs, passons à eux.


image


SurfMVP coordonné


image


SurfMVP coordonné est un modèle architectural dans lequel, contrairement à SurfMVP, nous avons supprimé l'entité Router qui se trouvait à l'intérieur de chaque module individuel. Le paradigme de la construction d'une application a un peu changé. Les modules ne sont plus totalement indépendants. Chaque module, à l'exception de ceux entièrement réutilisables, est situé dans un UserFlow séparé et distinct, qui, comme prévu, devrait effectuer une action générale conduisant l'utilisateur au résultat souhaité.


Un exemple d'un tel flux dans notre application est le flux de paiements. Les paiements sont un ensemble d'écrans qui permettent à l'utilisateur d'effectuer un virement ou un paiement de différentes manières.


Dans CoordinMed SurfMVP, l'entité Router a remplacé l'entité Coordinator , qui est désormais responsable de la navigation non seulement dans un module distinct, mais dans un ensemble de modules qui sont logiquement connectés les uns aux autres. Cela simplifie la navigation et l'utilisation de l'application. Schématiquement, notre application ressemble à ceci:


image


Tout en haut se trouve l'ApplicationCoordinator, qui est responsable du routage initial dans l'application. Par exemple, un cas, lorsque l'utilisateur est autorisé, nous l'enverrons immédiatement à la partie principale de l'application, sinon, nous l'enverrons à l'écran d'autorisation.


Si nous avons des liens profonds ou des notifications push dans notre application, nous pouvons toujours définir des règles d'initialisation et de démarrage pour les coordinateurs afin qu'ils construisent la pile directement au point D souhaité, dont nous avons parlé plus tôt.


image


Schématiquement, notre navigation ressemble maintenant à ceci. Chaque UserFlow individuel fait référence à son propre coordinateur, qui décide à son tour de ce qui se passera à l'avenir. La responsabilité de la transmission des données et de la poursuite de la navigation incombe désormais au coordinateur, il est déjà associé à d'autres modules ou d'autres coordinateurs pour continuer à construire la pile de navigation.


Avantages et inconvénients de SurfMVP coordonné


Avantages:


  1. Le principal avantage de l'approche coordinateur est la possibilité de réutiliser des blocs de navigation entiers dans l'application. Maintenant, de n'importe où dans l'application, il est possible d'appeler ce coordinateur et de ne penser à rien d'autre qu'à terminer son travail.
  2. Puisque la logique de navigation est isolée à l'intérieur d'un coordinateur séparé, il est maintenant beaucoup plus pratique de suivre la navigation: ouvrez simplement un fichier et l'image entière sous vos yeux. Il n'est plus nécessaire de percer tous les modules individuels pour comprendre ce qu'ils recherchent, assembler l'application et regarder la conception.
  3. Il est plus pratique de concevoir en grandes équipes. Il suffit au stade de la conception d'une nouvelle fonctionnalité distincte de prendre le temps de construire l'intégralité de la navigation et d'initialiser tous les modules, puis de déléguer le développement à un grand nombre de développeurs, et il y aura beaucoup moins de problèmes avec l'intégration de ces écrans entre eux.
  4. L'intégration de Deeplinks et de Push-Notifications n'est plus un casse-tête.

Inconvénients:


Comme pour toute approche architecturale, il y a des inconvénients à Coordinated SurfMVP.


  1. Les gros coordinateurs sont blessés. En raison de la concentration de toute la logique en un seul endroit, il devient beaucoup plus difficile de ne pas se noyer dans un grand nombre de lignes de code. Si vous ne suivez pas le principe de la responsabilité partagée, alors bien sûr, le coordinateur peut devenir un gros monstre, et tous les avantages de la lisibilité du code s'évaporeront facilement.
  2. Vous devez écrire beaucoup pour obtenir la beauté du code. En raison du grand nombre de couches dans l'application, chacune étant responsable d'une action distincte, vous devez traverser ces couches afin d'atteindre le coordinateur souhaité.
  3. Fuites de mémoire - le problème n'est pas nouveau, mais vous devez suivre cette question afin de ne pas entrer dans le trou. La principale raison des fuites de mémoire lorsque vous travaillez avec des coordinateurs est de conserver les cycles dans les rappels de module. Vous devez donc surveiller attentivement les liens solides à l'intérieur des fermetures.

Cas typique

Un cas typique est l'initialisation du nouveau coordinateur et la mise en œuvre de la fermeture finishFlow. La capture d'un weak coordinator est obligatoire, sinon le coordinateur se référera à lui-même, ce qui entraînera une fuite sous la forme d'AuthCoordinator.


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

Conclusions


Au stade de la conception, nous avons sous-estimé la complexité du projet et choisi la mauvaise approche architecturale. Mais cette erreur a permis de former un ensemble de règles et d'approcher plus soigneusement le choix de l'architecture lors de l'initialisation des projets.


Quand utiliser SurfMVP coordonné


En fait, quand vous le souhaitez, utilisez-le, mais nous respectons les conditions suivantes:


  • La structure des écrans est complexe et sujette à changement;
  • Il existe des liens profonds et / ou des notifications push avec navigation;
  • Il faut travailler dans une grande équipe;

Quand utiliser SurfMVP


Nous n'avons pas oublié notre premier modèle architectural. Nous l'utilisons toujours en studio lors du développement de projets s'il remplit les conditions suivantes:


  • Le projet est suffisamment petit et ne prévoit pas de se développer rapidement;
  • Le projet a une structure d'écran très simple et n'est pas soumis à de forts changements.

Matériel supplémentaire



Dans cet article, j'ai partagé un problème avec l'architecture que nous avons rencontrée en travaillant. Bien sûr, le choix de l'architecture à utiliser vous appartient. Dans le prochain article, je partagerai les problèmes de backend dans les grands projets et expliquerai comment nous les avons résolus. Restez à l'écoute!

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


All Articles