Una mirada honesta a la modularización de una gran aplicación de Android

Este artículo es el primer capítulo de una serie de varias partes que presenta el estudio de la modularización de una aplicación de Android a gran escala existente, destacando los desafíos y las ventajas y desventajas de un enfoque sobre otro.

En PagSeguro estamos a punto de migrar a un arquitecto modulartuEstás en nuestra aplicación PagBank de 4 años. La aplicación se lanzó en diciembre de 2016 y estaba destinada a ser una billetera digital simple con funcionalidad limitada. Era un producto bastante experimental y una oportunidad para aumentar la cartera de la empresa en ese momento, pero durante su viaje pasó de un simple prototipo a un producto utilizado por millones de personas.

La primera versión fue construida por solo dos equipos, con alrededor de 7 personas cada uno, y el tiempo de comercialización fue una estrategia clave para la empresa, que primero quería lanzar y luego iterar para obtener un producto adecuado para el mercado. Como la ambición de este proyecto era muy limitada al principio, la arquitectura, los modelos y las decisiones con respecto a la escalabilidad no eran un gran objetivo.

A medida que la aplicación creció, la empresa siguió su ejemplo, aumentando la actividad y la cantidad de equipos que la desarrollan. Esto ha supuesto una serie de desafíos como: coordinación, estandarización y calidad. Pronto, la arquitectura de la aplicación estaba comprometiendo la escalabilidad y la productividad de varios equipos que tenían que proporcionar un número cada vez mayor de funciones.

La aplicación se estructuró en un solo módulo con funcionalidad dividida por paquete, que ofrece una buena organización del código, pero carecía de los medios para hacer cumplir el desacoplamiento de flujos. También fue diseñado para tener una sola actividad y cada flujo se implementaría usando una pila de fragmentos coordinados de esta actividad principal. Debemos recordar que en ese momento aún no existía la Navegación (Componente Arquitectura), por lo que la abstracción para facilitar la navegación entre los fragmentos y su coordinación a través de la actividad principal era todo «a mano». Los singleton también se han utilizado ampliamente en toda la aplicación para facilitar el uso de abstracciones comunes, pero sin el objetivo de la capacidad de prueba y la separación de preocupaciones.

Esta estrategia es buena para aplicaciones pequeñas, ya que reduce la complejidad de la arquitectura, pero se amortiza rápidamente en aplicaciones grandes. Los principales problemas que queríamos abordar eran:

  • Acoplamiento flexible: queremos fortalecer la independencia de nuestros equipos, permitiéndoles desarrollar e implementar sus funciones sin tener que preocuparse por implementar otras partes de la aplicación.
  • Capacidad de prueba: Queremos tener una amplia cobertura de pruebas para que los desarrolladores tengan confianza en la implementación de nuevas funciones de que nada detendrá la aplicación.
  • Reutilización del código: para garantizar que las estructuras básicas se creen de manera que puedan reutilizarse en otras partes de la aplicación.
  • Rendimiento de la compilación y la entrega: a medida que la aplicación crece, el tiempo de creación y la duración de CI también aumentan. Queremos reducirlo y poder fortalecer la independencia del equipo a la hora de ofrecer nuevas funciones a nuestros usuarios.

La modularización fue una de las iniciativas que podría ayudarnos a abordar estos problemas. Cada función podría tener su propio módulo independiente y estar completamente desacoplado del resto de la aplicación, lo que afecta la capacidad de prueba y la independencia del equipo. Las estructuras centrales también podrían encapsularse en módulos separados e inyectarse como dependencias cuando sea necesario, abordando la reutilización del código. Finalmente, los tiempos de compilación y CI podrían beneficiarse del almacenamiento en caché compilando y probando solo los módulos que han cambiado en esa rama.

Dado que la modularización parece ser la solución adecuada para llevar la aplicación a su nueva era, estamos investigando diferentes enfoques, tratando de encontrar una dirección que satisfaga nuestras necesidades. Primero, enumeremos cuáles son estos requisitos:

  • Modularización por característica.
  • Arquitectura de múltiples actividades.
  • Una estructura de navegación sólida: la estructura debe ser fácil de extender y mantener.
  • Configuración sencilla de módulos: crear nuevos módulos e integrarlos con la aplicación debería ser lo más sencillo posible.
  • Prepárese para la entrega dinámica. No es un requisito para esta fase, pero la solución propuesta debe tener un buen ojo para su uso en funcionalidades dinámicas de modo que no sea necesaria una reestructuración de la arquitectura central cuando se adopte esta funcionalidad.
  • No confíe en la reflexión. El proyecto utiliza herramientas que ofuscan el código de producción y no queremos que la usabilidad de la estructura modular dependa de la necesidad de agregar reglas personalizadas para evitar la ofuscación.
  • Funciones para caracterizar la navegación.

Una vez establecidos estos requisitos, hemos revisado internamente nuestro escenario actual y planteado los siguientes puntos que pueden afectar la solución que estamos buscando:

  • Todas las funciones todavía están en el módulo: aplicación.
  • Actualmente, las funciones están divididas por paquete.
  • Enfoque de actividad múltiple con cada punto de entrada de una característica que es una actividad.
  • Algunas abstracciones básicas ya se han movido de la aplicación: por ejemplo: parte de seguridad, redes, persistencia, seguimiento / monitoreo y otras utilidades comunes.
  • Todavía hay algunas estructuras básicas altamente acopladas en el módulo: app.
  • Algunas características dependen directamente de las abstracciones de otras características.
  • Puede explorar las funciones.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *