Modular Mobile Apps

Don't Repeat Yourself

extreme version

Jarosław Michalik

Android developer

IoT and telemedicine

Organizer @ SFI IT Academic Festival

Java/Kotlin library - provides some functionality (e.x Retrofit)

can be used outside Android

Android library - provides views (e.x CircularImageView)

Module

Provides view, functionality and business logic

Can be built as a separate app with minimum effort

Features

  • news
  • trip planner
  • offers & beacons
  • weather
  • air pollution

Motivation

  • huge app (Smart City)
  • many major functionalities
  • different deployments
  • slightly different backend
  • lack of architect

Issues

  • each client wants something else
  • if(flavor==Milan) setOnClickListener{}
  • if(flavor==Malaga) icon.setTint()...
  • Gradle build finished in 43 minutes 17 seconds

Solution?

Gradle Flavors

change app ID, swap resources and API keys between flavors

                    
productFlavors {
    cityOne {
        applicationId "com.company.smartcity.cityOne"
        resValue("string", "app_name", "cityOne")
        versionCode 33
        versionName "$googlePlayVersion"
    }
    cityTwo {
        applicationId "com.company.smartcity.cityTwo"
        resValue("string", "app_name", "cityTwo")
        versionCode 20
        versionName "$googlePlayVersion"
    }
    cityThree {
        applicationId "com.company.smartcity.cityThree"
        resValue("string", "app_name", "cityThree")
        versionCode 8
        versionName "$googlePlayVersion"
    }
}
                    
                
app/src/cityOne/com/company/smartcity/Config.kt

                    
package com.company.smartcity

interface Config{
    val API_URL = "https://cityone.cityapp.com"
    val API_KEY = "Bearer AbCdEf123456"
}
                    
                
app/src/cityTwo/com/company/smartcity/Config.kt
                    
package com.company.smartcity

interface Config{
    val API_URL = "https://citytwo.cityapp.com"
    val API_KEY = "Bearer BaDcFe1324354"
}
                    
                

Not enough

New project - gamification in city app

Add new code to monolith app?

if else if else if else?

Create separate app (module) and integrate it with existing project

Requirements

  • screens: missions, leaderboards, tips & tricks, rewards
  • logic: send event "user is nearby XYZ beacon"
  • create entry from navigation drawer
  • provide dashboard elements (special offer and new missions)
  • integrate with existing notifications

compile only needed dependencies

                    
dependencies {
    cityOneImplementation project(":news")
}
                    
                

no unused code in classpath

Use Gradle Kotlin DSL to manage dependencies easier:

                        
dependencies {
    compile(projectConfiguration.libs.kotlinStdLib)

    testCompile(projectConfiguration.testLibs.junit)

    compile(supportDependencies.values())

    flavorCompile("wien", appModules["news"])
}


fun DependencyHandler.flavorCompile(
            flavorName: String,
            dependencyNotation: Any) =
        add("${flavorName}Compile", dependencyNotation)
                        
                    

create proxy classes as entry entry

                    
interface NewsProxy{
    fun provideSyncItem() : Single<SyncItem>
    fun provideDashboardItem() : DashboardItem
    fun provideMapElements() : Single<List<MapPinViewModel>>
}
                    
                

+dummy implementations

@Inject different dependencies in different Dagger modules

Avoid direct dependencies, rely on abstractions

dashboard as entry for other modules

                
interface DashboardProvider{
    fun getAll() : List
}

class DashboardProviderImpl : DashboardProvider{
    override fun getAll() = listOf(
                        NewsDashboardItem(),
                        TransitDashboardItem()
                    )
}

class DashboardProviderImpl : DashboardProvider{
    override fun getAll() = listOf(
                        NewsDashboardItem(),
                        GamificationItem(),
                        TransitDashboardItem()
                    )
}
                
            

different implementation for each flavor

new task: add mini article to gamification module

should gamification depend on news then?

make data model more generic

extract ArticleActivity from news module

build ArticleActivity as a Android library

add it as a dependency of news module and gamification module

Core module

each module relies on Core module

it provides common interfaces

Next steps

  • prepare gamification module and separate app
  • implement routing module
  • modularize news, transit and weather
  • reduce :app module

Smart City now

  • core + 4 modules
  • 5 deployments
  • ready to deploy new app in 2 weeks
  • city game became separate app for client demo
  • weather module app on Smartwatch
  • news module reused in different project

Each module has

  • networking
  • database
  • views
  • business logic

How to split app?

determine separate business logic and use cases

analyse your apps and extract common things

check your backend architecture

perform code analysis

https://codescene.io/

Tips & tricks

make sure that module does not rely on :app module

create separate Git repository

create mock backend for module (static json response)

...or ask your backend guys to deploy dedicated microservice

deploy AAR with module to internal Maven repo

Cons

  • bug in one module affects many apps
  • suddenly you maintain 7 small projects instead of one
  • difficult modularization if you have poor architecture
  • it takes time to modularize even well written app

Pros

  • modules can be maintained by different teams
  • decreased build times - gradle builds only module with applied changes
  • you can use different architecture in different modules - consider module blackbox
  • fast prototyping for demo
  • you can sell similar apps to different people
  • bug fixed in one module fixes other app

Thank you for attention

Studencki Festiwal Informatyczny

5 - 7 IV, Kraków

http://sfi.org.pl

Q&A

Jarosław Michalik

rozkmin.me
github.com/rozkminiacz