Problemas de arquitectura en grandes proyectos

El desarrollo de aplicaciones móviles parece una tarea bastante simple. Parecería qué hacer allí? Lancé un par de vistas, lo ungí con un poco de arquitectura, y eso es todo, el proyecto está listo, puede enviar la aplicación a la estación. En una serie de artículos compartiré las características que encontramos al desarrollar una aplicación para un banco grande.


Considere 5 temas importantes. Por supuesto, la mayoría de ellos se han discutido más de una vez en la comunidad, pero detrás de cada tema están el dolor, las lágrimas, el tiempo perdido y, lo más importante, la experiencia que nos ha sido útil, y espero que sea útil para usted.


imagen


Al comienzo del desarrollo de una aplicación móvil, el líder o diseñador se enfrenta a la pregunta: ¿qué patrón arquitectónico usar? Nuestro estudio tiene un patrón arquitectónico común MVP. El MVP puro es ciertamente bueno en su forma pura (ver imagen a continuación), pero no seríamos desarrolladores reales si no hubiéramos finalizado este patrón. No se detuvieron en una opción, y obtuvimos dos sucursales de MVP puro.


imagen


Por lo tanto, en la etapa de diseño, nos enfrentamos con la tarea de elegir uno y, sobre la base de un patrón arquitectónico generalmente aceptado y comprensible, seguir adelante. Pero, ya en una etapa tan temprana, logramos cometer un error, lo que posteriormente nos trajo muchos problemas.


Veamos dos de nuestros MVP en esteroides.


SurfMVP


imagen


La imagen muestra que, en comparación con el MVP habitual, no ha cambiado mucho. Notamos algunos problemas al cambiar de pantalla en aplicaciones iOS. Una gran cantidad de lógica para crear nuevas pantallas antes de que la transición se concentre directamente en el UIViewController, nos pareció que no estaba bien, por lo que lo primero que hicimos fue resaltar una entidad de enrutador separada, que es responsable de hacer las transiciones entre pantallas en la aplicación.
Los modelos en SurfMVP son los servicios que presenta el presentador para recuperar datos. A menudo, un servicio resuelve las tareas para todo el módulo, pero en situaciones difíciles tiene que interactuar con varios.
La entidad Configuradora es responsable de construir un módulo separado, inicializa todos los componentes necesarios y es responsable de crear dependencias entre ellos.


imagen


La característica principal de SurfMVP es que cada capa en MVP está separada por un protocolo. La imagen muestra un diagrama de las capas y la relación de los protocolos entre ellas. Se necesitan protocolos para que cada capa se separe de la otra y, en teoría, se reemplace fácilmente. Cada una de las capas no debe revelar detalles de implementación.


Consideremos por separado:


ViewInput : implementa View en sí, Presenter mantiene el enlace. Este protocolo describe los métodos mediante los cuales Presenter puede controlar la Vista , transferir datos, cambiar estados, etc.


ViewOutput : implementa Presenter , View tiene un enlace. El protocolo describe un conjunto de acciones que pueden ocurrir en la Vista y métodos de ciclo de vida, por ejemplo, eventos de interacción del usuario con la pantalla.


RouterInput : implementa Router y Presenter mantiene un enlace a él, ya que es el único responsable de iniciar la navegación adicional en la aplicación.


ModuleTransitionable : la vista está implementada, el enrutador mantiene un enlace. Este es el único protocolo "básico" en SurfMVP . Es necesario para proporcionar al enrutador un conjunto de métodos para trabajar con la navegación de aplicaciones.


ModuleInput - Implementa el presentador . Este protocolo debe contener métodos por los cuales otro módulo que contiene un enlace a este protocolo podría cambiar el estado del módulo actual.


ModuleOutput : implementa el presentador del módulo de llamada, el enlace contiene el presentador del módulo llamado. Si la pantalla de perfil se puede mostrar desde el módulo de noticias, NewsPresenter debe implementar ProfileModuleOutput y ProfilePresenter debe contener un enlace.
ModuleOutput se pasa al Configurador del módulo llamado y se instala allí en Presenter . Contiene métodos de módulo que afectan el comportamiento del módulo de llamada.


Problema SurfMVP


Basado en todo lo anterior, hay un problema principal: la navegación. Aunque el mensaje para resaltar una entidad de enrutador separada era problemas de navegación, resultó que desaparecieron, pero no por mucho tiempo. SurfMVP se ha utilizado con éxito en proyectos con navegación plana simple, sin enlaces profundos complejos y notificaciones push.


La siguiente imagen muestra esquemáticamente la navegación en la aplicación con SurfMVP. Cada módulo individual se comunica con el otro a través de su propio enrutador. Por lo tanto, se construye la navegación de cualquier flujo en la aplicación.


imagen


Este tipo de navegación es muy adecuado para el caso en que el usuario realiza el flujo de su elección en la aplicación. Por ejemplo, en la imagen a continuación: el usuario recorre las pantallas desde el punto A hasta el punto D, por lo que él mismo construye una pila sobre la que debe ir y regresar de la misma manera.


imagen


Los problemas comienzan en el momento en que es necesario transferir al usuario del punto A al punto D, y esto debería suceder sin su participación. Por ejemplo, si un usuario hace clic en Push-Notification o sigue un enlace de una aplicación de terceros. En el caso de SurfMVP, tendremos que agregar un enrutador global que controlará la navegación independientemente de dónde se encuentre el usuario actualmente en la aplicación. Para resolver este problema globalmente, decidimos usar los coordinadores, pasemos a ellos.


imagen


SurfMVP coordinado


imagen


Coordinated SurfMVP es un patrón arquitectónico en el que, a diferencia de SurfMVP, eliminamos la entidad de enrutador que estaba dentro de cada módulo individual. El paradigma de construir una aplicación ha cambiado un poco. Los módulos ya no son completamente independientes. Cada módulo, con la excepción de los completamente reutilizables, se encuentra en un UserFlow separado que, según lo planeado, debe realizar alguna acción general que lleve al usuario al resultado deseado.


Un ejemplo de tal flujo en nuestra aplicación es el flujo de pagos. Los pagos son un conjunto de pantallas que permiten al usuario realizar una transferencia o pago de diferentes maneras.


En Coordinated SurfMVP, la entidad Router reemplazó a la entidad Coordinator , que ahora es responsable de navegar no solo un módulo separado, sino un conjunto de módulos que están conectados lógicamente entre sí. Esto simplifica la navegación y el trabajo con la aplicación. Esquemáticamente, nuestra aplicación se ve así:


imagen


En la parte superior está el Coordinador de la aplicación, que es responsable del enrutamiento inicial en la aplicación. Por ejemplo, un caso, cuando el usuario está autorizado, lo enviaremos inmediatamente a la parte principal de la aplicación; de lo contrario, lo enviaremos a la pantalla de autorización.


Si tenemos enlaces profundos o notificaciones push en nuestra aplicación, siempre podemos establecer reglas de inicialización e inicio para los coordinadores para que construyan la pila directamente en el punto D deseado, del que hablamos anteriormente.


imagen


Esquemáticamente, nuestra navegación ahora se ve así. Cada UserFlow individual se refiere a su propio coordinador, quien a su vez decide lo que sucederá en el futuro. La responsabilidad de transmitir datos e iniciar una navegación adicional ahora recae en el coordinador, él ya está asociado con otros módulos u otros coordinadores para continuar construyendo la pila de navegación.


Pros y contras de SurfMVP coordinado


Ventajas:


  1. La principal ventaja del enfoque coordinador es la capacidad de reutilizar bloques de navegación completos dentro de la aplicación. Ahora, desde cualquier lugar de la aplicación, es posible llamar a este coordinador y no pensar en otra cosa que no sea completar su trabajo.
  2. Dado que la lógica de navegación está aislada dentro de un coordinador separado, ahora es mucho más conveniente seguir la navegación: simplemente abra un archivo y la imagen completa ante sus ojos. Ya no es necesario perforar todos los módulos individuales para comprender lo que están buscando, armar la aplicación y observar el diseño.
  3. Es más conveniente diseñar en equipos grandes. Es suficiente en la etapa de diseño de una nueva característica separada para asignar tiempo para construir toda la navegación e inicializar todos los módulos, después de lo cual delegará el desarrollo a un gran número de desarrolladores, y habrá muchos menos problemas con la integración de estas pantallas entre sí.
  4. La integración de enlaces profundos y notificaciones push ya no es un dolor de cabeza.

Desventajas


Como con cualquier enfoque arquitectónico, hay desventajas en Coordinated SurfMVP.


  1. Los grandes coordinadores duelen. Debido a la concentración de toda la lógica en un solo lugar, se vuelve mucho más difícil no ahogarse en una gran cantidad de líneas de código. Si no sigue el principio de responsabilidad compartida, entonces, por supuesto, el coordinador puede convertirse en un gran monstruo, y todas las ventajas de la legibilidad del código se evaporarán fácilmente.
  2. Tienes que escribir mucho para lograr belleza en el código. Debido a la gran cantidad de capas en la aplicación, cada una de las cuales es responsable de una acción separada, debe atravesar estas capas para llegar al coordinador deseado.
  3. Fugas de memoria: el problema no es nuevo, pero debe seguir este asunto para no meterse en el agujero. La razón principal de las pérdidas de memoria cuando se trabaja con coordinadores es conservar los ciclos en las devoluciones de llamada del módulo. Por lo tanto, debe controlar cuidadosamente los fuertes vínculos dentro de los cierres.

Caso típico

Un caso típico es la inicialización del nuevo Coordinador y la implementación del cierre finishFlow. Capturar a un weak coordinator es obligatorio, de lo contrario, el Coordinador se referirá a sí mismo, lo que provocará una fuga en forma de Coordinador de Auth.


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

Conclusiones


En la etapa de diseño, subestimamos la complejidad del proyecto y elegimos el enfoque arquitectónico incorrecto. Pero este error ayudó a formar un conjunto de reglas y a abordar con más cuidado la elección de la arquitectura al inicializar proyectos.


Cuando usar SurfMVP coordinado


De hecho, cuando lo desee, úselo, pero cumplimos con las siguientes condiciones:


  • La estructura de las pantallas es compleja y está sujeta a cambios;
  • Hay enlaces profundos y / o notificaciones push con navegación;
  • Es necesario trabajar en un gran equipo;

Cuando usar SurfMVP


No nos olvidamos de nuestro primer patrón arquitectónico. Todavía lo usamos en el estudio cuando desarrollamos proyectos si cumple las siguientes condiciones:


  • El proyecto es lo suficientemente pequeño y no planea desarrollarse rápidamente;
  • El proyecto tiene una estructura de pantalla muy simple y no está sujeto a cambios fuertes.

Materiales adicionales



En este artículo, compartí un problema con la arquitectura que encontramos al trabajar. Por supuesto, la elección de qué arquitectura usar queda con usted. En el próximo artículo compartiré los problemas de backend en grandes proyectos y contaré cómo los resolvimos. Estén atentos!

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


All Articles