Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Successfully modularizing your app

Jeroen Mols
October 03, 2019

Successfully modularizing your app

Video: https://www.youtube.com/watch?v=9eikhwWehWk

Modularizing your app seems to be all the hype these days. But why should you actually care? What are the benefits for you and your team? How should a modularized app look like? And how do you start splitting your app?

In this talk, you will learn why modularization matters and how a modularized architecture looks like. We'll study a real-life example project and investigate how modules are set up, organized and configuration is shared across modules. Finally, you'll learn how you can start modularizing your existing app and I'll share what lessons we learned while doing so at Philips Hue.

Articles:
1. why - http://bit.ly/modularization_why
2. architecture - http://bit.ly/modularization_architecture
3. example - http://bit.ly/modularization_example
4. how - http://bit.ly/modularization_how
5. lessons - http://bit.ly/modularization_lessons

Source code: http://bit.ly/modularization_code #androiddev #mobiconf

Jeroen Mols

October 03, 2019
Tweet

More Decks by Jeroen Mols

Other Decks in Programming

Transcript

  1. @MOLSJEROEN
    SUCCESSFULLY
    MODULARIZING APPS

    View Slide

  2. @MOLSJEROEN
    @MOLSJEROEN
    @MOLSJEROEN

    View Slide

  3. @MOLSJEROEN
    WHY
    MODULARIZATION ARCHITECTURE
    HOW TO APPROACH
    LESSONS LEARNED

    View Slide

  4. WHY

    View Slide

  5. @MOLSJEROEN
    1. SPEED UP BUILDS
    Gradle does two things to speed up builds
    • Avoid work/caching
    • Parallellization
    Need smaller items of work

    View Slide

  6. @MOLSJEROEN
    1. SPEED UP BUILDS: SMALLER ITEMS OF WORK

    View Slide

  7. @MOLSJEROEN
    1. SPEED UP BUILDS: SINGLE MODULE

    View Slide

  8. @MOLSJEROEN
    1. SPEED UP BUILDS: MULTI MODULE

    View Slide

  9. @MOLSJEROEN
    1. SPEED UP BUILDS: MULTI MODULE

    View Slide

  10. @MOLSJEROEN
    2. ALLOW ON DEMAND DELIVERY

    View Slide

  11. View Slide

  12. @MOLSJEROEN
    2. ALLOW ON DEMAND DELIVERY
    At-install delivery
    On demand delivery
    Conditional delivery
    Instant delivery
    Need to split up app
    Support future requirements

    View Slide

  13. @MOLSJEROEN
    3. SIMPLIFY DEVELOPMENT
    APP

    View Slide

  14. @MOLSJEROEN
    3. SIMPLIFY DEVELOPMENT
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  15. IT’S EASIER TO BUILD SEVERAL SMALL
    THINGS THAN TO BUILD ONE BIG THING
    Unknown

    View Slide

  16. @MOLSJEROEN
    3. SIMPLIFY DEVELOPMENT
    Clear contracts between modules
    • Less spaghetti code
    • Fewer cascading changes
    • Easier in-module refactoring
    Easy to understand code
    Cheaper maintenance

    View Slide

  17. @MOLSJEROEN
    4. REUSE MODULES

    View Slide

  18. @MOLSJEROEN
    4. REUSE MODULES: MULTIPLE APPS
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    APP 2

    View Slide

  19. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    APP 2
    4. REUSE MODULES: MULTIPLE APPS

    View Slide

  20. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    APP 2
    FEATURE X
    FEATURE X
    4. REUSE MODULES: MULTIPLE APPS

    View Slide

  21. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    APP 2
    FEATURE X
    FEATURE X
    4. REUSE MODULES: MULTIPLE APPS

    View Slide

  22. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    SDK LIBRARY 2
    4. REUSE MODULES: SDK

    View Slide

  23. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    SDK LIBRARY 2
    4. REUSE MODULES: SDK

    View Slide

  24. @MOLSJEROEN
    APP 1
    EXPOSED FEATURE
    WITH INTENT
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    4. REUSE MODULES: EXPOSE FEATURES

    View Slide

  25. @MOLSJEROEN
    APP 1
    EXPOSED FEATURE
    WITH INTENT
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    4. REUSE MODULES: EXPOSE FEATURES

    View Slide

  26. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    AWESOME
    GITHUB LIBRARY LIBRARY 2
    4. REUSE MODULES: OPEN SOURCE

    View Slide

  27. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    AWESOME
    GITHUB LIBRARY LIBRARY 2
    4. REUSE MODULES: OPEN SOURCE

    View Slide

  28. @MOLSJEROEN
    5. EXPERIMENT WITH NEW TECHNOLOGIES
    Rapidly evolving Android landscape
    Need to experiment rapidly
    Cost of wrong technology choice
    Avoid technology lock-in

    View Slide

  29. @MOLSJEROEN
    Source: https://blog.daftcode.pl/hype-driven-development-3469fc2e9b22

    View Slide

  30. @MOLSJEROEN
    5. EXPERIMENT WITH NEW TECHNOLOGIES
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  31. @MOLSJEROEN
    6. SCALING
    Large teams cause new issues
    • Merge conflicts
    • Dependencies
    • Code ownership

    View Slide

  32. @MOLSJEROEN
    6. SCALING: DEVELOP IN PARALLEL
    Team A Team B
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  33. @MOLSJEROEN
    6. SCALING: DEVELOP IN PARALLEL
    Team A Team B
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  34. @MOLSJEROEN
    6. SCALING: OUTSOURCE
    Team A External
    company
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  35. @MOLSJEROEN
    7. IMPROVE LEGACY CODE

    View Slide

  36. @MOLSJEROEN
    Aggressive
    High impact
    Lot of work
    Not value focussed
    Risky launch
    Exciting
    Less risky
    Gradual
    Slow
    Tedious
    Continuous releases
    Unsatisfying
    REWRITE REFACTOR
    V S

    View Slide

  37. @MOLSJEROEN
    Aggressive
    High impact
    Lot of work
    Not value focussed
    Risky launch
    Exciting
    Less risky
    Gradual
    Slow
    Tedious
    Continuous releases
    Unsatisfying
    REWRITE REFACTOR
    V S

    View Slide

  38. @MOLSJEROEN
    7. IMPROVE LEGACY CODE: REWRITE, REFACTOR OR ACCEPT
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  39. @MOLSJEROEN
    7. IMPROVE LEGACY CODE: REWRITE, REFACTOR OR ACCEPT
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  40. @MOLSJEROEN
    7. IMPROVE LEGACY CODE
    Able to rewrite parts of code base
    Keep ability to release
    Gradually roll out possible
    Faster time to market

    View Slide

  41. @MOLSJEROEN
    8. SIMPLIFY TEST AUTOMATION

    View Slide

  42. @MOLSJEROEN
    8. SIMPLIFY TEST AUTOMATION

    View Slide

  43. @MOLSJEROEN
    8. SIMPLIFY TEST AUTOMATION
    Speeds up tests
    Simplified test setup
    Higher test reliability

    View Slide

  44. @MOLSJEROEN
    WHY: RECAP
    Speed up builds
    Allow on demand delivery
    Simplify development
    Reuse modules across apps
    Experiment with new technologies
    Scale development team
    Improve legacy code
    Simplify test automation

    View Slide

  45. MODULARIZATION ARCHITECTURE

    View Slide

  46. YOU HAVE YOUR WAY. I HAVE MY WAY.
    THE RIGHT WAY, THE CORRECT WAY, AND
    THE ONLY WAY DOES NOT EXIST.
    F. Nietzsche

    View Slide

  47. @MOLSJEROEN
    APP STRUCTURE

    View Slide

  48. @MOLSJEROEN
    APP STRUCTURE

    View Slide

  49. @MOLSJEROEN
    APP STRUCTURE

    View Slide

  50. @MOLSJEROEN
    APP STRUCTURE

    View Slide

  51. FEATURES ARE A FLOW OF SCREENS

    View Slide

  52. @MOLSJEROEN
    APP WIREFRAME

    View Slide

  53. @MOLSJEROEN
    APP WIREFRAME

    View Slide

  54. @MOLSJEROEN
    APP WIREFRAME

    View Slide

  55. APPS CONSIST OF SEVERAL FEATURES

    View Slide

  56. @MOLSJEROEN
    ANDROID APP COMPONENTS
    ACTIVITY SERVICE BROADCAST RECEIVER CONTENT PROVIDER

    View Slide

  57. @MOLSJEROEN
    ANDROID APP COMPONENTS
    Full screen UI
    Reuse across apps
    Started via explicit or implicit intents
    ACTIVITY SERVICE BROADCAST RECEIVER CONTENT PROVIDER

    View Slide

  58. @MOLSJEROEN
    IMPLICIT INTENTS

    View Slide

  59. ANDROID LINKS MULTIPLE APPS
    TOGETHER VIA IMPLICIT INTENTS

    View Slide

  60. @MOLSJEROEN
    MODULARIZED ARCHITECTURE
    APP

    View Slide

  61. @MOLSJEROEN
    MODULARIZED ARCHITECTURE
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  62. @MOLSJEROEN
    MODULARIZED ARCHITECTURE
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  63. @MOLSJEROEN
    FEATURE MODULES
    Full screen, coherent user facing functionality in the app
    Android library module
    Single activity with (optional) navigation graph
    Respond to implicit intents and pass back a result
    Never depend on other features or app
    Depend on several library modules
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  64. @MOLSJEROEN
    LIBRARY MODULES
    Plumbing that is reused across several or all features
    Android library, pure Java or pure Kotlin module
    Never depend on features or app
    Can (but don’t have to) depend on other libraries
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  65. @MOLSJEROEN
    APP MODULE
    Links feature modules together in a useful app
    Android application module
    Depends on other features and libraries
    Orchestrates navigation between features
    Decides what features are enabled using feature toggles
    Doesn’t contain much code
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  66. @MOLSJEROEN
    ADVANTAGES
    Application structure
    Navigation
    Testing
    Scaling
    Feature flagging
    Experiment with new technology

    View Slide

  67. @MOLSJEROEN
    APPLICATION STRUCTURE

    View Slide

  68. @MOLSJEROEN
    DASHBOARD FEATURE

    View Slide

  69. @MOLSJEROEN
    DASHBOARD ACTIVITY

    View Slide

  70. @MOLSJEROEN
    LOGIN FEATURE

    View Slide

  71. @MOLSJEROEN
    SHARING FEATURE

    View Slide

  72. @MOLSJEROEN
    NAVIGATION
    Split navigation in smaller parts
    • Within a feature -> handled by the feature
    • Between features -> handled by the app module
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6

    View Slide

  73. @MOLSJEROEN
    NAVIGATION: IN FEATURE
    Android Navigation components
    • Visualize navigation flow
    • Find back screens
    • Easy code browsing

    View Slide

  74. @MOLSJEROEN
    NAVIGATION: IN FEATURE

    View Slide

  75. @MOLSJEROEN
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/login_graph"/>
    NAVIGATION: IN FEATURE

    View Slide

  76. @MOLSJEROEN
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/login_graph"/>
    NAVIGATION: IN FEATURE

    View Slide

  77. @MOLSJEROEN
    android:id="@+id/login_graph"
    app:startDestination="@id/welcomeFragment">
    android:id="@+id/welcomeFragment"
    android:name="modularization.features.login.WelcomeFragment" />
    android:id="@+id/loginFragment"
    android:name="modularization.features.login.LoginFragment" />
    android:id="@+id/avatarFragment"
    android:name="modularization.features.login.AvatarFragment"/>

    NAVIGATION: IN FEATURE

    View Slide

  78. @MOLSJEROEN
    android:id="@+id/login_graph"
    app:startDestination="@id/welcomeFragment">
    android:id="@+id/welcomeFragment"
    android:name="modularization.features.login.WelcomeFragment" />
    android:id="@+id/loginFragment"
    android:name="modularization.features.login.LoginFragment" />
    android:id="@+id/avatarFragment"
    android:name="modularization.features.login.AvatarFragment"/>

    NAVIGATION: IN FEATURE

    View Slide

  79. @MOLSJEROEN
    android:id="@+id/login_graph"
    app:startDestination="@id/welcomeFragment">
    android:id="@+id/welcomeFragment"
    android:name="modularization.features.login.WelcomeFragment" />
    android:id="@+id/loginFragment"
    android:name="modularization.features.login.LoginFragment" />
    android:id="@+id/avatarFragment"
    android:name="modularization.features.login.AvatarFragment"/>

    NAVIGATION: IN FEATURE

    View Slide

  80. @MOLSJEROEN
    //...
    android:id="@+id/welcomeFragment"
    android:name="modularization.features.login.WelcomeFragment" >
    android:id="@+id/action_welcomeFragment_to_loginFragment"
    app:destination="@id/loginFragment" />




    NAVIGATION: IN FEATURE

    View Slide

  81. @MOLSJEROEN
    //...
    android:id="@+id/welcomeFragment"
    android:name="modularization.features.login.WelcomeFragment" >
    android:id="@+id/action_welcomeFragment_to_loginFragment"
    app:destination="@id/loginFragment" />




    NAVIGATION: IN FEATURE

    View Slide

  82. @MOLSJEROEN
    findNavController().navigate(R.id.action_welcomeFragment_to_loginFragment)
    NAVIGATION: IN FEATURE

    View Slide

  83. @MOLSJEROEN
    NAVIGATION: IN FEATURE

    View Slide

  84. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    startActivity(Intent(activity, DashboardActivity::class.java))

    View Slide

  85. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    startActivity(Intent("action.dashboard.open"))

    View Slide

  86. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="modularization.dashboard">









    View Slide

  87. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="modularization.dashboard">









    View Slide

  88. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    String duplication of action
    In depth knowledge of intent creation
    Other apps could also handle action

    View Slide

  89. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    ACTIONS LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  90. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    object Actions {
    fun openLoginIntent() = Intent("action.login.open")
    fun openDashboardIntent() = Intent("action.dashboard.open")
    fun openSharingIntent() = Intent("action.sharing.open")
    }
    activity.startActivity(Actions.openDashboardIntent())

    View Slide

  91. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    object Actions {
    fun openLoginIntent() = Intent("action.login.open")
    fun openDashboardIntent() = Intent("action.dashboard.open")
    fun openSharingIntent() = Intent("action.sharing.open")
    }
    activity.startActivity(Actions.openDashboardIntent())

    View Slide

  92. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    object Actions {
    fun openDashboardIntent(userId: String) =
    Intent(context, "action.dashboard.open")
    .putExtra(EXTRA_USER, UserArgs(userId))
    }
    activity.startActivity(Actions.openDashboardIntent("userId"))

    View Slide

  93. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    object Actions {
    fun openDashboardIntent(userId: String) =
    Intent(context, "action.dashboard.open")
    .putExtra(EXTRA_USER, UserArgs(userId))
    }
    activity.startActivity(Actions.openDashboardIntent("userId"))

    View Slide

  94. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES

    View Slide

  95. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    object Actions {
    fun openLoginIntent(context: Context) =
    internalIntent(context, "action.login.open")
    private fun internalIntent(context: Context, action: String) =
    Intent(action).setPackage(context.packageName)
    }

    View Slide

  96. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    object Actions {
    fun openLoginIntent(context: Context) =
    internalIntent(context, "action.login.open")
    private fun internalIntent(context: Context, action: String) =
    Intent(action).setPackage(context.packageName)
    }

    View Slide

  97. @MOLSJEROEN
    NAVIGATION: BETWEEN FEATURES
    Implicit intents
    Actions module
    Abstract away feature parameters
    Restrict to current package

    View Slide

  98. @MOLSJEROEN
    TESTING
    UNIT
    INTEGRATION
    E2E

    View Slide

  99. @MOLSJEROEN
    TESTING
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  100. @MOLSJEROEN
    FEATURE TESTING
    class LoginFlowTest {
    @Rule
    @JvmField
    var mActivityTestRule = ActivityTestRule(LoginActivity::class.java)
    @Test
    fun loginFlowTest() {
    onView(withId(R.id.button_login_start)).perform(click())
    onView(withId(R.id.button_login_signin)).perform(click())
    onView(withId(R.id.button_login_toapp)).check(matches(isDisplayed()))
    }
    }

    View Slide

  101. @MOLSJEROEN
    FEATURE TESTING
    class LoginFlowTest {
    @Rule
    @JvmField
    var mActivityTestRule = ActivityTestRule(LoginActivity::class.java)
    @Test
    fun loginFlowTest() {
    onView(withId(R.id.button_login_start)).perform(click())
    onView(withId(R.id.button_login_signin)).perform(click())
    onView(withId(R.id.button_login_toapp)).check(matches(isDisplayed()))
    }
    }

    View Slide

  102. @MOLSJEROEN
    FEATURE TESTING
    class LoginFlowTest {
    @Rule
    @JvmField
    var mActivityTestRule = ActivityTestRule(LoginActivity::class.java)
    @Test
    fun loginFlowTest() {
    onView(withId(R.id.button_login_start)).perform(click())
    onView(withId(R.id.button_login_signin)).perform(click())
    onView(withId(R.id.button_login_toapp)).check(matches(isDisplayed()))
    }
    }

    View Slide

  103. @MOLSJEROEN
    APP FLOW TESTING
    class AppFlowTest {
    @Rule
    @JvmField
    var mActivityTestRule = ActivityTestRule(MainActivity::class.java)
    @Test
    fun test_criticalUserFlow_throughoutEntireApp() {
    onView(withId(modularization.login.R.id.button_login_start)).perform(click())
    onView(withId(modularization.login.R.id.button_login_signin)).perform(click())
    onView(withId(modularization.login.R.id.button_login_toapp)).perform(click())
    onView(withId(R.id.action_albums)).perform(click())
    onView(withId(R.id.action_sharing)).perform(click())
    onView(withId(R.id.button_social_facebook)).perform(click())
    onView(withId(R.id.recyclerView_sharing_contacts)).check(matches(isDisplayed()))
    }
    }

    View Slide

  104. @MOLSJEROEN
    SCALING: TEAMS
    Team A Team B
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  105. @MOLSJEROEN
    APP 1
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 1 LIBRARY 2
    APP 2
    FEATURE X
    FEATURE X
    SCALING: APPS

    View Slide

  106. @MOLSJEROEN
    SCALING: SDKS
    APP
    EXPOSED FEATURE
    WITH INTENT
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 AWESOME GITHUB LIBRARY
    LIB 1 SDK LIBRARY 3

    View Slide

  107. @MOLSJEROEN
    SCALING: TECHNOLOGY
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  108. @MOLSJEROEN
    IMPROVE LEGACY
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  109. @MOLSJEROEN
    IMPROVE LEGACY: REWRITE FEATURE
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3

    View Slide

  110. @MOLSJEROEN
    IMPROVE LEGACY: REWRITE FEATURE
    APP
    FEATURE 1
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    LIBRARY 4 LIBRARY 4
    LIB 1 LIBRARY 2 LIBRARY 3
    FEATURE 5*

    View Slide

  111. @MOLSJEROEN
    IMPROVE LEGACY: FEATURE FLAGS
    object Actions {
    fun openLoginIntent() =
    if (FeatureFlag.loginRewrite) { Intent("action.login2.open") }
    else { Intent("action.login.open") }
    }

    View Slide

  112. @MOLSJEROEN
    IMPROVE LEGACY: FEATURE FLAGS
    object Actions {
    fun openLoginIntent() =
    if (FeatureFlag.loginRewrite) { Intent("action.login2.open") }
    else { Intent("action.login.open") }
    }

    View Slide

  113. @MOLSJEROEN
    IMPROVE LEGACY: ROLL OUT

    View Slide

  114. @MOLSJEROEN

    View Slide

  115. @MOLSJEROEN
    ARCHITECTURE: RECAP
    Easy code navigation
    Simple in-feature and across feature navigation
    Better test automation
    Enables scaling
    Able to experiment with new technologies
    Staged rollout of rewritten features

    View Slide

  116. HOW TO APPROACH

    View Slide

  117. @MOLSJEROEN
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE.
    FEATURE
    FUTURE
    FEATURE
    FUTURE
    FEATURE
    OLD APP
    LIB LIBRARY FUTURE LIBRARY
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    LIB 1
    LIB FUTURE LIBRARY
    LIBRARY
    CHOOSE YOUR SIDE

    View Slide

  118. @MOLSJEROEN
    PULL CODE UP
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    OLD APP
    LIB LIBRARY FUTURE LIBRARY
    Pull modules up

    View Slide

  119. @MOLSJEROEN
    PULL CODE UP
    Conceptually very simple
    Extract features without dependency issues
    Huge upfront change
    Encourages gradual modularization
    Incompatible with Butterknife: change to R2.id.***
    Features need to be extracted before libraries

    View Slide

  120. @MOLSJEROEN
    PUSH CODE DOWN
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    LIB 1
    LIB FUTURE LIBRARY
    LIBRARY
    Push modules down

    View Slide

  121. @MOLSJEROEN
    PUSH CODE DOWN
    Initially a lot harder
    Extract libraries without dependency issues
    No big upfront change
    Forces aggressive modularisation
    Better grip on modularisation process
    Bottom up code clean up

    View Slide

  122. @MOLSJEROEN
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE.
    FEATURE
    FUTURE
    FEATURE
    FUTURE
    FEATURE
    OLD APP
    LIB LIBRARY FUTURE LIBRARY
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    LIB 1
    LIB FUTURE LIBRARY
    LIBRARY
    CHOOSE YOUR SIDE

    View Slide

  123. @MOLSJEROEN
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE.
    FEATURE
    FUTURE
    FEATURE
    FUTURE
    FEATURE
    OLD APP
    LIB LIBRARY FUTURE LIBRARY
    APP
    FEATURE
    FEATURE
    FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    FUTURE FEATURE
    LIB 1
    LIB FUTURE LIBRARY
    LIBRARY
    CHOOSE YOUR SIDE

    View Slide

  124. @MOLSJEROEN
    CONSIDERATIONS
    Make a big initial push
    Aggressively restrict visibility
    Combine with general code improvements
    Postpone/avoid problems
    • Cut dependencies using abstractions
    • Deprecate and rewrite
    • Clean up internals later

    View Slide

  125. WHEN PLANNING IS LONG THEN IT’S
    WRONG, IF IT’S TIGHT THEN IT’S RIGHT
    Elon musk

    View Slide

  126. LESSONS LEARNED

    View Slide

  127. @MOLSJEROEN
    CONFIGURE MODULES
    Adding a new module must be easy
    Maintaining module configurations must be easy

    View Slide

  128. @MOLSJEROEN
    CONFIGURE MODULES
    subprojects {
    afterEvaluate { project ->
    if (project.hasProperty('android')) {
    android {
    buildToolsVersion Config.buildTools
    compileSdkVersion Config.compileSdk
    defaultConfig {
    minSdkVersion Config.minSdk
    targetSdkVersion Config.targetSdk
    }
    compileOptions {
    sourceCompatibility Config.javaVersion
    targetCompatibility Config.javaVersion
    } } } } }

    View Slide

  129. @MOLSJEROEN
    CONFIGURE MODULES
    subprojects {
    afterEvaluate { project ->
    if (project.hasProperty('android')) {
    android {
    buildToolsVersion Config.buildTools
    compileSdkVersion Config.compileSdk
    defaultConfig {
    minSdkVersion Config.minSdk
    targetSdkVersion Config.targetSdk
    }
    compileOptions {
    sourceCompatibility Config.javaVersion
    targetCompatibility Config.javaVersion
    } } } } }

    View Slide

  130. @MOLSJEROEN
    CONFIGURE MODULES
    subprojects {
    afterEvaluate { project ->
    if (project.hasProperty('android')) {
    android {
    buildToolsVersion Config.buildTools
    compileSdkVersion Config.compileSdk
    defaultConfig {
    minSdkVersion Config.minSdk
    targetSdkVersion Config.targetSdk
    }
    compileOptions {
    sourceCompatibility Config.javaVersion
    targetCompatibility Config.javaVersion
    } } } } }

    View Slide

  131. @MOLSJEROEN
    CONFIGURE MODULES
    apply plugin: 'com.android.library'
    apply plugin: 'kotlin-android-extensions'
    apply plugin: 'kotlin-android'
    dependencies {
    implementation project(':libraries:ui-components')
    implementation project(':libraries:actions')
    implementation Deps.androidx_material
    implementation Deps.androidx_constraintlayout
    implementation Deps.androidx_navigation_fragment
    implementation Deps.androidx_navigation_ui
    testImplementation Deps.testlib_junit
    androidTestImplementation Deps.testandroidx_runner
    androidTestImplementation Deps.testandroidx_rules
    androidTestImplementation Deps.testandroidx_espressocore
    }

    View Slide

  132. @MOLSJEROEN
    CONFIGURE MODULES

    View Slide

  133. @MOLSJEROEN
    MODULE GRAPH
    apply from: 'https://raw.githubusercontent.com/JakeWharton/SdkSearch/master/
    gradle/projectDependencyGraph.gradle'
    ./gradlew projectDependencyGraph

    View Slide

  134. @MOLSJEROEN
    MODULE GRAPH

    View Slide

  135. @MOLSJEROEN
    ORGANIZE SETTINGS.GRADLE
    include ':app', ':features:login', ':features:dashboard', ':features:sharing', ':libraries:ui-
    components', ':libraries:actions'

    View Slide

  136. @MOLSJEROEN
    ORGANIZE SETTINGS.GRADLE
    include ':app'
    include ':features:login'
    include ':features:dashboard'
    include ':features:sharing'
    include ':libraries:ui-components'
    include ':libraries:actions'

    View Slide

  137. @MOLSJEROEN
    ORGANIZE SETTINGS.GRADLE
    include ':app'
    include ':features:login'
    include ':features:dashboard'
    include ':features:sharing'
    include ':libraries:ui-components'
    include ':libraries:actions'

    View Slide

  138. @MOLSJEROEN
    MODULE INTERNALS

    View Slide

  139. @MOLSJEROEN
    MODULE INTERNALS - ORGANIZATION

    View Slide

  140. @MOLSJEROEN
    MODULE INTERNALS - ORGANIZATION

    View Slide

  141. @MOLSJEROEN
    MODULE INTERNALS - PACKAGE NAME
    features: [project-name].features.[feature-name]

    e.g. modularization.features.login
    libraries: [project-name].libraries.[library-name]

    e.g. modularization.libraries.actions

    View Slide

  142. @MOLSJEROEN
    MODULE INTERNALS - PACKAGE NAME
    import android.content.Intent
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import modularization.features.login.LoginActivity
    import modularization.features.sharing.SharingActivity
    import modularization.features.dashboard.DashboardActivity
    import modularization.libraries.actions.Actions
    import modularization.libraries.uicomponents.RoundedButton

    View Slide

  143. @MOLSJEROEN
    LAYOUT PREVIEWS

    View Slide

  144. @MOLSJEROEN
    LAYOUT PREVIEWS

    View Slide

  145. @MOLSJEROEN
    LAYOUT PREVIEWS
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="modularization.features.dashboard">




    View Slide

  146. @MOLSJEROEN
    LAYOUT PREVIEWS
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="modularization.features.dashboard">




    View Slide

  147. @MOLSJEROEN
    LAYOUT PREVIEWS

    View Slide

  148. @MOLSJEROEN
    DEPENDENCY MANAGEMENT
    object Config {
    val minSdk = 23
    val javaVersion = JavaVersion.VERSION_1_8
    val buildTools = "28.0.3"
    }
    object Versions {
    val androidx_core = "1.0.1"
    val androidx_nav = "2.0.0"
    ...
    }
    object Deps {
    val androidx_core = "androidx.core:core-ktx:${Versions.androidx_core}"
    val androidx_nav_fragment = "androidx.navigation:navigation-fragment-ktx:${Versions.androidx_nav}"
    val androidx_nav_ui = “androidx.navigation:navigation-ui-ktx:${Versions.androidx_nav}"
    ...
    }

    View Slide

  149. @MOLSJEROEN
    CONFIGURE MODULES
    apply plugin: 'com.android.library'
    apply plugin: 'kotlin-android-extensions'
    apply plugin: 'kotlin-android'
    dependencies {
    implementation project(':libraries:ui-components')
    implementation project(':libraries:actions')
    implementation Deps.androidx_material
    implementation Deps.androidx_constraintlayout
    implementation Deps.androidx_navigation_fragment
    implementation Deps.androidx_navigation_ui
    testImplementation Deps.testlib_junit
    androidTestImplementation Deps.testandroidx_runner
    androidTestImplementation Deps.testandroidx_rules
    androidTestImplementation Deps.testandroidx_espressocore
    }

    View Slide

  150. @MOLSJEROEN
    DEPENDENCY MANAGEMENT
    ./gradlew dependencyUpdates

    View Slide

  151. @MOLSJEROEN
    SPEED UP BUILDS
    dependencies {
    // Avoid doing this
    api project(':libraries:ui-components')
    // Do this instead
    implementation project(':libraries:ui-components')
    }

    View Slide

  152. @MOLSJEROEN
    API VS IMPLEMENTATION DEPENDENCY
    APP

    View Slide

  153. @MOLSJEROEN
    API DEPENDENCY
    APP

    View Slide

  154. @MOLSJEROEN
    API DEPENDENCY
    APP

    View Slide

  155. @MOLSJEROEN
    API DEPENDENCY
    APP

    View Slide

  156. @MOLSJEROEN
    IMPLEMENTATION DEPENDENCY
    APP

    View Slide

  157. WRAP UP

    View Slide

  158. THERE ARE NO BIG PROBLEMS, THERE
    ARE JUST A LOT OF LITTLE PROBLEMS.
    Henry Ford

    View Slide

  159. @MOLSJEROEN
    MODULARIZATION HAS TONS OF BENEFITS
    APP FEATURES LIBRARIES
    NAVIGATION WITHIN AND BETWEEN FEATURES
    PUSH CODE DOWN
    SIMPLIFY ADDING MODULES

    View Slide

  160. @MOLSJEROEN

    View Slide

  161. @MOLSJEROEN
    HTTPS://JEROENMOLS.COM/BLOG

    View Slide

  162. @MOLSJEROEN
    REFERENCES
    Blog posts:
    1. http://bit.ly/modularization_why
    2. http://bit.ly/modularization_architecture
    3. http://bit.ly/modularization_example
    4. http://bit.ly/modularization_how
    5. http://bit.ly/modularization_lessons
    http://bit.ly/modularization_code

    View Slide

  163. @MOLSJEROEN
    IMAGE CREDITS
    Welcome image by Clement127
    https://flic.kr/p/n9ctTn
    Official Android documentation
    https://d.android.com
    Font awesome
    https://fontawesome.com/

    View Slide

  164. MOLSJEROEN

    View Slide