RIB de arquitectura móvil multiplataforma de Uber

El 20 de diciembre de 2016, los chicos de Uber Engineering publicaron un artículo sobre la nueva arquitectura (aquí está la traducción de este artículo en el centro). Les presento la traducción de la parte principal de la documentación.

¿Para qué sirve la arquitectura RIB?


RIBs es un marco de arquitectura multiplataforma de Uber. Fue diseñado para grandes aplicaciones móviles con una gran cantidad de estados integrados.

Al desarrollar este marco, los ingenieros de Uber se adhirieron a los siguientes principios:

  • Soporte para la colaboración entre personas que se desarrollan en diferentes plataformas: la gran mayoría de las partes complejas de las aplicaciones de Uber son similares en iOS y Android. Las RIB proporcionan patrones de desarrollo comunes para Android e iOS. Al usar RIB, los ingenieros de iOS y Android pueden compartir una arquitectura desarrollada conjuntamente para sus funciones.
  • Minimización de los estados y decisiones globales : los cambios de estado globales pueden conducir a un comportamiento impredecible y pueden hacer imposible saber a qué conducirán estos o aquellos cambios en el código del programa. La arquitectura basada en RIB fomenta estados encapsulados en una jerarquía profunda de RIB bien aisladas para evitar problemas con los estados globales.
  • Capacidad de prueba y aislamiento: las clases deben ser simples para poder escribir pruebas unitarias, y también tener una razón para estar aisladas (refiriéndose a SRP ). Las clases RIB individuales tienen diferentes responsabilidades (por ejemplo, enrutamiento, lógica de negocios, lógica de presentación, creación de otras clases RIB). Además, la lógica de la RIB primaria está básicamente separada de la lógica de la RIB secundaria. Esto facilita la prueba de clases RIB y reduce la dependencia entre los componentes del sistema.
  • Herramientas para el desarrollo productivo: tomar prestados patrones arquitectónicos no triviales puede generar problemas con el crecimiento de la aplicación si no hay herramientas confiables para soportar la arquitectura. La arquitectura RIB viene con herramientas IDE para crear código, análisis estático e integración de tiempo de ejecución, lo que mejora la productividad del desarrollador en equipos grandes y pequeños.
  • El principio de apertura-cercanía: los desarrolladores, si es posible, deberían agregar nuevas funciones sin cambiar el código existente. Cuando se usan RIB, la implementación de esta regla se puede ver en varios lugares. Por ejemplo, puede adjuntar o crear una RIB secundaria compleja que requiera dependencias de su RIB principal, con poco o ningún cambio en la RIB principal.
  • Estructuración en torno a la lógica empresarial: la estructura de la lógica empresarial de una aplicación no debe reflejar estrictamente la estructura de la interfaz de usuario. Por ejemplo, para facilitar la animación y el rendimiento de una vista, la jerarquía de la vista puede ser más pequeña que la jerarquía RIB. O, una sola función RIB puede controlar la apariencia de tres vistas que aparecen en diferentes lugares en la interfaz de usuario.
  • Contratos exactos: los requisitos deben declararse utilizando contratos que se verifican en tiempo de compilación. No se debe compilar una clase si no se satisfacen sus propias dependencias, así como las dependencias de invitado. La arquitectura RIB utiliza ReactiveX para representar las dependencias del huésped, los sistemas de inyección de dependencia de tipo seguro ( DI ) para representar las dependencias de clase y muchas otras características de DI para ayudar a crear invariantes de datos.

Elementos componentes RIB


Si anteriormente trabajó con la arquitectura VIPER , las clases que componen la RIB le resultarán familiares. Las RIB generalmente consisten en los siguientes elementos, cada uno de los cuales se implementa en su propia clase:



Interactractor


Interactor contiene lógica de negocios. En esta clase, se suscriben notificaciones Rx, se toman decisiones sobre cambiar el estado, almacenar datos y adjuntar RIB secundarias.

Todas las operaciones realizadas en Interactor deben limitarse a su ciclo de vida. Uber ha creado un conjunto de herramientas para garantizar que la lógica de negocios se ejecute solo con interacción activa. Esto evita que los Interactores se desactiven, pero las suscripciones Rx aún se activan y provocan actualizaciones no deseadas de la lógica empresarial o el estado de la interfaz de usuario.

Enrutador


El enrutador monitorea los eventos de Interactor y los convierte en adjuntar y desconectar RIB secundarias. El enrutador existe por tres razones simples:

  • El enrutador existe como un objeto pasivo, lo que simplifica la prueba de la lógica compleja de Interactor sin la necesidad de crear apéndices para interactuadores secundarios o de alguna otra manera para cuidar su existencia.
  • Los enrutadores crean una capa adicional de abstracción entre los interactuadores primarios y secundarios. Esto hace que la comunicación sincrónica entre interactuadores sea un poco más compleja y fomenta el uso de comunicaciones Rx en lugar de comunicaciones directas entre RIB.
  • Los enrutadores contienen una lógica de enrutamiento simple y repetitiva que de otro modo se implementaría en Interactors. Portar este código repetitivo a los enrutadores ayuda a los interactianos a ser pequeños y más centrados en la lógica comercial central de RIB.

Constructor


Se necesita Builder para crear instancias para todas las clases incluidas en la RIB, así como crear instancias de Builders para las RIB secundarias.

Al resaltar la lógica de creación de clases en Builder, se agrega soporte para la capacidad de crear apéndices en iOS y hace que el resto del código RIB sea insensible a los detalles de la implementación DI. Builder es la única parte de la RIB que necesita conocer el sistema DI utilizado en el proyecto. Al implementar otro generador, puede reutilizar el resto del código RIB en el proyecto utilizando un mecanismo DI diferente.

Presentador


Presentador es una clase sin estado que traduce un modelo de negocio en un modelo de presentación y viceversa. Se puede usar para facilitar la prueba de transformaciones de vista de modelo. Sin embargo, a menudo esta traducción es tan trivial que no justifica la creación de una clase de presentador separada. Si Presenter no se realiza, la traducción de los modelos de vista se convierte en responsabilidad de View (Controller) o Interactor.

Vista (controlador)


Ver crea y actualiza la interfaz de usuario. Incluye crear y organizar los componentes de la interfaz, manejar la interacción del usuario, completar los componentes de la interfaz de usuario con datos y animación. La vista está diseñada para ser lo más "tonta" (pasiva) posible. Simplemente muestran información. En general, no contienen ningún código para el que se deben escribir las pruebas unitarias.

Componente


El componente se usa para administrar dependencias RIB. Ayuda al generador a crear instancias de las otras clases que componen el RIB. El componente proporciona acceso a las dependencias externas necesarias para crear la RIB, así como a sus propias dependencias creadas por la propia RIB, y controla el acceso a ellas desde otras RIB. El componente de la RIB principal normalmente está incrustado en el RIB-Builder secundario para proporcionarle acceso a las dependencias de la RIB principal.

Gestión del estado


El estado de la aplicación se gestiona y representa principalmente mediante RIB que están conectadas actualmente al árbol RIB. Por ejemplo, a medida que un usuario pasa por diferentes estados en una aplicación de viaje conjunta simplificada, la aplicación conecta y desconecta los siguientes RIB:



Las RIB solo toman decisiones estatales dentro de su competencia. Por ejemplo, el LoggedIn RIB solo toma la decisión de hacer la transición entre estados como Request y OnTrip. No toma ninguna decisión sobre cómo debería ser el comportamiento del sistema cuando estamos en la pantalla de OnTrip.

No todos los estados se pueden guardar agregando o quitando RIB. Por ejemplo, cuando cambia la configuración del perfil de usuario, el RIB no se vincula ni se desconecta. Como regla, guardamos este estado dentro de los flujos de modelos inmutables, que reenvían valores al cambiar partes. Por ejemplo, el nombre de usuario se puede almacenar en el archivo ProfileDataStream, que está bajo la competencia de LoggedIn. Solo las respuestas de red tienen acceso de escritura a esta secuencia. Estamos pasando una interfaz que proporciona acceso de lectura a estos hilos por el gráfico DI.

No hay nada en las RIB que sea la verdad última para el estado de las RIB. Esto contrasta con el hecho de que los marcos más magistrales como React ya se proporcionan de fábrica. En el contexto de cada RIB, puede elegir patrones que faciliten el flujo de datos unidireccionales, o puede dejar que el estado de la lógica empresarial y el estado de vista se desvíen temporalmente de la norma para aprovechar los marcos de animación eficientes para la plataforma.

Interacción entre costillas


Cuando Interactor toma una decisión de lógica de negocios, es posible que deba informar a la otra RIB sobre eventos, como la finalización y el envío de datos. El marco RIB no incluye ninguna forma única de transferir datos entre RIB. Sin embargo, este método está diseñado para facilitar algunos patrones comunes.

Como regla general, si la conexión se reduce al RIB secundario, transmitimos esta información como eventos en el flujo Rx. O bien, los datos se pueden incluir como un parámetro en el método build () del RIB secundario, en cuyo caso este parámetro se convierte en una invariante durante toda la vida del secundario.



Si la conexión sube por el árbol RIB al RIB Interactor padre, esta conexión se realiza a través de la interfaz de escucha, ya que el RIB padre puede tener un ciclo de vida más largo que el RIB hijo. Una RIB principal, o algún objeto en su gráfico DI, implementa una interfaz de escucha y la coloca en su gráfico DI para que sus RIB secundarias puedan llamarlo. El uso de esta plantilla para transferir datos en sentido ascendente en lugar de hacer que las RIB principales se suscriban directamente a las secuencias Rx de sus RIB secundarias tiene varias ventajas. Evita las pérdidas de memoria, le permite escribir, probar y mantener las RIB principales sin saber qué RIB secundarias están conectadas a ellas, y también reduce la cantidad de problemas necesarios para conectar / desconectar una RIB secundaria. Las transmisiones o escuchas Rx no necesitan cancelar el registro o volver a registrarse con este método de adjuntar una RIB secundaria.



Kit de herramientas de RIB


Para garantizar una implementación sin problemas de la arquitectura RIB en las aplicaciones, los ingenieros de Uber han creado herramientas para simplificar el uso de RIB y utilizar invariantes creados al implementar la arquitectura RIB. El código fuente de este kit de herramientas estaba parcialmente abierto y se menciona en los ejemplos (vea la parte correcta - aprox. Por.).

El kit de herramientas, que actualmente es de código abierto, incluye:


Kit de herramientas para el que Uber planea abrir código fuente en el futuro:

  • Analizador estático para evitar varias pérdidas de memoria en RIB
  • Integración de RIB con un detector de fugas de memoria durante la ejecución del programa
  • (Android) Procesadores de anotaciones para pruebas más fáciles
  • (Android) Analizador estático RxJava que proporciona RIB con vistas que no han cambiado desde el hilo principal

PS


En sports.ru realmente nos gustó el enfoque de los ingenieros de Uber, porque muchas veces encontramos todos los problemas arquitectónicos que describió el artículo. A pesar de lo razonable, RIB tiene una serie de desventajas, por ejemplo, un umbral bastante alto para ingresar a la arquitectura. Analizaremos con más detalle los pros y los contras de la arquitectura en los siguientes artículos, se planean al menos dos: para iOS y para Android. Para aquellos que quieran sumergirse en RIB en este momento, hay una columna en la página wiki a la derecha que tiene lecciones de inglés. Por mi cuenta, observo que la arquitectura nació claramente en largas discusiones técnicas y reuní las mejores prácticas para construir arquitecturas para aplicaciones móviles que están actualmente disponibles. Y finalmente, un poco de relaciones públicas: en sports.ru también nos encantan las discusiones técnicas, a menudo organizamos talleres técnicos para colegas, estudiamos regularmente nuevas tecnologías y, en general, tenemos un gran ambiente. Entonces, si quieres formar parte de nuestro equipo, ¡ bienvenido !

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


All Articles