Scalable architecture for large mobile applications

In this article, we will not parse MVP, MVVM, MVI, or something like that. Today we’ll talk about a more global thing than just presentation-level architecture. How to design a really large application in which tens or hundreds of developers can work comfortably? An application that is easy to extend no matter how much code we have written.


Requirements for large projects:


  1. Weak code connectivity. Any changes should affect as little code as possible.
  2. Reusing code. Identical things should be easy to reuse without copy-past.
  3. Ease of expansion. It should be easy for a developer to add new functionality to existing code.
  4. Stability. Any new code can be easily disabled using feature toggles , especially if you are using trunk-based development .
  5. Code ownership. The project should be divided into modules, so that it would be easy to assign an owner for each module. This will help us in the code review phase. And here not only about large things, like Gradle / Pods modules, but also ordinary features, which can have different owners .

Component


image


. - (MV*) Presenter/ViewModel/Interactor/- . , - - . , , , // . , -, .


, . .


image



  1. . - .
  2. . .
  3. . .
  4. . , , .
  5. UI. .
  6. Unidirectional data flow . .
  7. . feature toggles.
    , , , . , - .


image


  1. (DomainObject).
  2. (UI State).
  3. .
  4. - ( , ), (Action) , . UI State, (Action) , Service. Service (. 1).


, , (Actions) (Service). Service (DomainObjects) . Service - : UserService, PaymentsService, CartService, : ProductDetailsService, OrderService.


image


, , MVP/MVC/MVVM/MVI , ().



β€Šβ€”β€Š , , , , .


:


  • Middlewareβ€Šβ€”β€Š (Actions). , Middleware . .
  • Reducerβ€Šβ€”β€Š Middleware. .

image


, Middleware Reducer , Middleware Reducer.
: Flux, Redux, MVI


Server Drive UI


, (DomainObject), . Play Store/App Store. , !


image


From the server we can receive a list of components on the screen indicating their type, position on the screen, version, and data necessary for operation and display.


{
  "components": [
    {
      "type": "toolbar",
      "version": 3,
      "position": "header",
      "data": {
        "title": "Profile",
        "showUpArrow": true
      }
    },
    {
      "type": "user_info",
      "version": 1,
      "position": "header",
      "data": {
        "id": 1234,
        "first_name": "Alexey",
        "last_name": "Glukharev"
      }
    },
    {
      "type": "user_photo",
      "position": "header",
      "version": 2,
      "data": {
        "user_photo": "https://image_url.png"
      }
    },
    {
      "type": "menu_item",
      "version": 1,
      "position": "content",
      "data": {
        "text": "open user details",
        "deeplink": "app://user/detail/1234"
      }
    },
    {
      "type": "menu_item",
      "version": 1,
      "position": "content",
      "data": {
        "text": "contact us",
        "deeplink": "app://contact_us"
      }
    },
    {
      "type": "button",
      "version": 1,
      "position": "bottom",
      "data": {
        "text": "log out",
        "action": "log_out"
      }
    }
  ]
}

Questions?


I participated in the development of several large projects using this architecture, and I am open both to questions on architecture in general and to the technical details of Android implementation.


All Articles