⬆️ ⬇️

Uber Cross-Platform Mobile Architecture RIBs

December 20, 2016, guys from Uber Engineering published an article about a new architecture (here is a translation of this article in Habré). I present to your attention the translation of the main part of the documentation.



What is RIBs architecture all about?



RIBs is a cross-platform architectural framework from Uber. It was designed for large mobile applications with a large number of nested states.



When developing this structure, Uber engineers adhered to the following principles:

')



Constituent elements of RIBs



If you have previously worked with the VIPER architecture, then the classes that are part of the RIB will look familiar to you. RIBs typically consist of the following elements, each of which is implemented in its class:







Interactor



Interactor contains business logic. In this class, a subscription to Rx notifications occurs, decisions are made on state changes, data storage and attachment of child RIBs.



All operations performed in Interactor should be limited to its life cycle. Uber has created a toolkit to ensure that business logic is executed only with active interaction. This prevents Interactors from being deactivated, but Rx subscriptions still work and cause unwanted updates to the business logic or state of the user interface.



Router



Router tracks events from Interactor and converts these events into attaching and detaching child RIBs. Router exists for three simple reasons:





Builder



Builder is needed to create instances for all classes included in the RIB, as well as create instances of Builders for child RIBs.



Highlighting class creation logic in Builder adds support for stub creation in iOS and makes the rest of the RIB code insensitive to the details of the DI implementation. Builder is the only part of the RIB that needs to be aware of the DI system used in the project. By implementing another Builder, you can reuse the rest of the RIB code in a project using a different DI mechanism.



Presenter



Presenter is a stateless class that translates a business model into a presentation model and vice versa. It can be used to facilitate the testing of view model transformations. However, this translation is often so trivial that it does not justify the creation of a separate class Presenter. If Presenter is not done, the translation of the view models becomes the responsibility of the View (Controller) or Interactor.



View (Controller)



View creates and updates the user interface. It includes the creation and arrangement of interface components, the processing of user interaction, the filling of user interface components with data, and animation. View is designed to be as “stupid” (passive) as possible. They simply display information. In general, they do not contain any code for which unit tests should be written.



Component



Component is used to manage RIB dependencies. It helps Builder create instances of other classes that make up the RIB. The component provides access to external dependencies required to create a RIB, as well as its own dependencies created by the RIB itself, and control access to them from other RIBs. A parent RIB component is typically embedded in a child RIB Builder to give the child RIB access to the dependencies of the parent RIB.



State management



The state of the application is mainly controlled and presented by the RIBs that are currently connected to the RIB tree. For example, as the user navigates through various states in a simplified application for collaborative travel, the application appends and separates the following RIBs:







RIBs only make state decisions within their competence. For example, LoggedIn RIB only decides to transition between states such as Request and OnTrip. It does not make any decisions about how the system should behave when we are on the OnTrip screen.



Not all states can be saved by adding or removing RIBs. For example, when user profile settings change, the RIB is not associated or disconnected. As a rule, we save this state inside streams of immutable models that re-send values ​​when parts change. For example, a user name can be stored in a ProfileDataStream file that is within the scope of LoggedIn. Only network responses have write access to this stream. We pass an interface that provides read access to these threads down the DI column.



There is nothing in RIBs that is the ultimate truth for the state of RIB. This contrasts with the fact that more masterful frameworks, such as React, are already provided out of the box. In the context of each RIB, you can choose templates that facilitate a unidirectional data flow, or you can let the state of business logic and the state of the view temporarily deviate from the norm to take advantage of effective platform animation frameworks.



Interaction between RIBs



When Interactor makes a decision related to business logic, it may need to inform the other RIB about events, such as the completion and sending of data. RIB framework does not include any single way to transfer data between RIBs. However, this method was created in order to facilitate some common patterns.



As a rule, if the link goes down to the child RIB, then we pass this information as events in the Rx stream. Or, the data can be included as a parameter in the build () method of the child RIB, in which case this parameter becomes an invariant for the lifetime of the child.







If the link goes up the RIB tree to the parent RIB Interactor, then this link is made through the listener interface, since the parent RIB can have a longer life cycle than the child RIB. The parent RIB, or some object on its DI graph, implements the listener interface and places it on its DI graph, so that its child RIBs can call it. Using this template to send data up instead of parent RIBs directly subscribing to their child RIBs Rx streams has several advantages. It prevents memory leaks, allows you to write, test and maintain parent RIBs without knowing which child RIBs are attached to them, and also reduces the amount of fuss necessary to attach / detach a child RIB. Rx streams or listeners do not need to unregister or re-register with this method of attaching a child RIB.







Rib toolkit



To ensure the smooth implementation of the RIB architecture in applications, Uber engineers have created tools to simplify the use of RIB and the use of invariants created by implementing the RIB architecture. The source code of this toolkit was partially opened and is mentioned in the examples (see the right part - comment. Per.).



The toolkit, which is currently open source, includes:





Tools where Uber plans to open source code in the future:





PS



We in sports.ru really liked the approach of Uber engineers, because we have many times faced with all the architectural problems that the article described. Despite the forethought, RIB has a number of drawbacks, for example, a rather high threshold for entering the architecture. We will analyze in more detail the pros and cons of the architecture in the following articles; at least two of them are planned - for iOS and for Android. For those who want to dive into the RIB right now, there is a column on the wiki page on which there are lessons in English. From myself, I note that the architecture was clearly born in long technical discussions and gathered in itself the best practices of building architectures for mobile applications that are currently available. And finally, a bit of PR - we also like technical discussions at sports.ru , often hold technical workshops for colleagues, regularly learn new technologies and in general we have a cool atmosphere. So if you want to be part of our team - welcome !

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



All Articles