Modular Architecture for a Large-Scale Mobile Application

Facundo Rodríguez
Flux IT Thoughts
Published in
5 min readJun 25, 2020

--

When we embarked on a project with one of our most important clients from the banking sector, we came across a way of working organized into squads or cells, in which each squad is made up of a team of 5 or 6 people with different profiles, which allows its autonomous operation within the organization.

In this article, I’ll share the road we’ve covered developing a native iOS and Android mobile application, as well as the decisions we’ve taken to succeed in the implementation of native CI/CD on the squads model.

Squads

Each squad was in charge of developing some of the mobile app’s functionalities, but the features must be assembled to build applications that can be deployed in the stores (Play Store and App Store). This methodology called for the modularization of the native development.

Modules

To simplify the work division, different functional modules were created by a particular squad. This team was responsible for module development, testing and building.

In order to be independent of the application, each module had its own Git repository, which also defined entry points to functionalities that can be turned on or off during runtime (feature toggle or feature flag).

We used Kotlin as a programming language for Android and we used Swift for iOS. Moreover, we used Gradle and Cocoapod to manage dependencies.

We also created our own module and application archetypes to simplify the building and to include guidelines in the examples (Gradel archetypes for Android and Cocoapod templates for iOS).

Application

In both platforms, the application has minimal logic to start its modules and to manage the multi-environment configuration (dev, test and production). Modules are incorporated into the application as dependencies (Gradle and Cocoapod) with a particular version.

We allowed the use of local dependencies: developers clone the application from Git repositories and the modules they need to work with when they create the workspace. In that case, the dependencies of the developing modules are handled (with a reference to the path where the code of the module is) and it doesn´t require a fixed version (for both Android and iOS platforms)

The rest of the functional modules of another squad are regarded as a library with a fixed version. Thus, developers, in contrast with the previous case, won’t work on those modules and they need a stable version of the other teams’ modules.

Decisions

When it comes to the decisions we took to carry out the modularization, we highlight:

  • The need to be homogeneous both for iOS and Android, so that both versions of the application are as similar as possible as regards implementation, simplifying its understanding for any dev.
  • Develop a native core module, which addresses needs common to all modules, and which includes: networking, internal and analytics events, security, dependencies injection, feature flag management, among others.
  • Mobile CI/CD: we’ve analyzed several SaaS tools to implement CI/CD. Of all the available options, we chose Bitrise for the automated construction of modules and native applications. Its simplicity was the distinctive feature we valued the most. As regards Bitrise workflows, we took into account the building, the unit tests and the deployment for all its components. As regards mobile applications, we created a workflow for the deployment to Google Play Alpha (Android) and TestFlight (iOS).
  • Use of feature flags: Feature flags allow you to “turn on/off” an application functionality during runtime (without deploying new code). We chose Split.io to manage the application’s feature flags. Each module exposes its main functionalities, which have feature flags linked to them. Feature toggles at the application level were also defined, since they allow us to control external situations, such as mandatory updates.
  • Avoid interdependencies between modules, to ease the development of the app. Only 3 common modules were created (UI, commons and core) and dependencies among functional modules weren’t allowed.

We defined internal events, the navigation component,and dependencies injection with public interfaces in order to avoid physical dependencies among modules.

CI/CD with Bitrise

When we started using Bitrise in the application and all the modules, we discovered a simple tool that offers several benefits: it detects the kind of application and creates primary default workflows. It also incorporates several pre-configurable steps for the creation of customized workflows. If there isn’t any step that solves a particular task, you can always resort to a bash script to get the job done.

Bitrise also has its own application store to keep all the artifacts built by the workflow (.ipa, .apk, etc.) and they can be shared from the internal store with the QA and dev teams.

In order to encourage the reuse of workflows and to avoid repeating the configuration in each of the app’s modules, we’ve implemented generic workflows. The idea is to create a Git repository with different yml files that define entire workflows of workflow fragments. Then, from the project where we want to use them (a specific module or application), we can invoke them through a series of parameters. This guarantees their reuse and simplifies the general maintenance within the tool.

Finally, for each push in the Git repositories, several triggers in Bitrise (using Gitlab webhooks) were configured to set off the workflow’s execution, which, according to the branch or if it is a tag, automatically test, build and publish modules and applications.

Conclusion

The project’s native module development entailed a significant first decision and it triggered many challenges that we managed to overcome due to the decisions we made. However, it also involved great benefits, allowing us to:

  • Work with a large-scale team on only one app, without it becoming chaotic.
  • Create functional expertise when it comes to the team, focusing on a reduced scope within the business logic.
  • Hide the module’s internal implementation details and gain flexibility when it comes to developing.
  • Simplify the incorporation of new functionalities.

Most certainly, a key aspect for successful mobile app modularization is to rely on a continuous integration and continuous deployment (CI/CD) platform that streamlines and simplifies the module and application building, and we discovered Bitrise as a powerful ally to fulfill that task.

Learn more about Flux IT: Website · Instagram · LinkedIn · Twitter · Dribbble · Breezy

--

--