@MOLSJEROEN
WHY
MODULARIZATION ARCHITECTURE
HOW TO APPROACH
LESSONS LEARNED
Slide 4
Slide 4 text
WHY
Slide 5
Slide 5 text
@MOLSJEROEN
1. SPEED UP BUILDS
Gradle does two things to speed up builds
• Avoid work/caching
• Parallellization
Need smaller items of work
Slide 6
Slide 6 text
@MOLSJEROEN
1. SPEED UP BUILDS: SMALLER ITEMS OF WORK
Slide 7
Slide 7 text
@MOLSJEROEN
1. SPEED UP BUILDS: SINGLE MODULE
Slide 8
Slide 8 text
@MOLSJEROEN
1. SPEED UP BUILDS: MULTI MODULE
Slide 9
Slide 9 text
@MOLSJEROEN
1. SPEED UP BUILDS: MULTI MODULE
Slide 10
Slide 10 text
@MOLSJEROEN
2. ALLOW ON DEMAND DELIVERY
Slide 11
Slide 11 text
No content
Slide 12
Slide 12 text
@MOLSJEROEN
2. ALLOW ON DEMAND DELIVERY
At-install delivery
On demand delivery
Conditional delivery
Instant delivery
Need to split up app
Support future requirements
@MOLSJEROEN
5. EXPERIMENT WITH NEW TECHNOLOGIES
Rapidly evolving Android landscape
Need to experiment rapidly
Cost of wrong technology choice
Avoid technology lock-in
@MOLSJEROEN
5. EXPERIMENT WITH NEW TECHNOLOGIES
APP
FEATURE 1
FEATURE 2
FEATURE 3
FEATURE 4
FEATURE 5
FEATURE 6
Slide 31
Slide 31 text
@MOLSJEROEN
6. SCALING
Large teams cause new issues
• Merge conflicts
• Dependencies
• Code ownership
Slide 32
Slide 32 text
@MOLSJEROEN
6. SCALING: DEVELOP IN PARALLEL
Team A Team B
APP
FEATURE 1
FEATURE 2
FEATURE 3
FEATURE 4
FEATURE 5
FEATURE 6
Slide 33
Slide 33 text
@MOLSJEROEN
6. SCALING: DEVELOP IN PARALLEL
Team A Team B
APP
FEATURE 1
FEATURE 2
FEATURE 3
FEATURE 4
FEATURE 5
FEATURE 6
Slide 34
Slide 34 text
@MOLSJEROEN
6. SCALING: OUTSOURCE
Team A External
company
APP
FEATURE 1
FEATURE 2
FEATURE 3
FEATURE 4
FEATURE 5
FEATURE 6
Slide 35
Slide 35 text
@MOLSJEROEN
7. IMPROVE LEGACY CODE
Slide 36
Slide 36 text
@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
Slide 37
Slide 37 text
@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
@MOLSJEROEN
7. IMPROVE LEGACY CODE
Able to rewrite parts of code base
Keep ability to release
Gradually roll out possible
Faster time to market
Slide 41
Slide 41 text
@MOLSJEROEN
8. SIMPLIFY TEST AUTOMATION
Slide 42
Slide 42 text
@MOLSJEROEN
8. SIMPLIFY TEST AUTOMATION
Slide 43
Slide 43 text
@MOLSJEROEN
8. SIMPLIFY TEST AUTOMATION
Speeds up tests
Simplified test setup
Higher test reliability
Slide 44
Slide 44 text
@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
Slide 45
Slide 45 text
MODULARIZATION ARCHITECTURE
Slide 46
Slide 46 text
YOU HAVE YOUR WAY. I HAVE MY WAY.
THE RIGHT WAY, THE CORRECT WAY, AND
THE ONLY WAY DOES NOT EXIST.
F. Nietzsche
Slide 47
Slide 47 text
@MOLSJEROEN
APP STRUCTURE
Slide 48
Slide 48 text
@MOLSJEROEN
APP STRUCTURE
Slide 49
Slide 49 text
@MOLSJEROEN
APP STRUCTURE
Slide 50
Slide 50 text
@MOLSJEROEN
APP STRUCTURE
Slide 51
Slide 51 text
FEATURES ARE A FLOW OF SCREENS
Slide 52
Slide 52 text
@MOLSJEROEN
APP WIREFRAME
Slide 53
Slide 53 text
@MOLSJEROEN
APP WIREFRAME
Slide 54
Slide 54 text
@MOLSJEROEN
APP WIREFRAME
Slide 55
Slide 55 text
APPS CONSIST OF SEVERAL FEATURES
Slide 56
Slide 56 text
@MOLSJEROEN
ANDROID APP COMPONENTS
ACTIVITY SERVICE BROADCAST RECEIVER CONTENT PROVIDER
Slide 57
Slide 57 text
@MOLSJEROEN
ANDROID APP COMPONENTS
Full screen UI
Reuse across apps
Started via explicit or implicit intents
ACTIVITY SERVICE BROADCAST RECEIVER CONTENT PROVIDER
Slide 58
Slide 58 text
@MOLSJEROEN
IMPLICIT INTENTS
Slide 59
Slide 59 text
ANDROID LINKS MULTIPLE APPS
TOGETHER VIA IMPLICIT INTENTS
@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
Slide 64
Slide 64 text
@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
Slide 65
Slide 65 text
@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
Slide 66
Slide 66 text
@MOLSJEROEN
ADVANTAGES
Application structure
Navigation
Testing
Scaling
Feature flagging
Experiment with new technology
Slide 67
Slide 67 text
@MOLSJEROEN
APPLICATION STRUCTURE
Slide 68
Slide 68 text
@MOLSJEROEN
DASHBOARD FEATURE
Slide 69
Slide 69 text
@MOLSJEROEN
DASHBOARD ACTIVITY
Slide 70
Slide 70 text
@MOLSJEROEN
LOGIN FEATURE
Slide 71
Slide 71 text
@MOLSJEROEN
SHARING FEATURE
Slide 72
Slide 72 text
@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
Slide 73
Slide 73 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Android Navigation components
• Visualize navigation flow
• Find back screens
• Easy code browsing
Slide 74
Slide 74 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 75
Slide 75 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 76
Slide 76 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 77
Slide 77 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 78
Slide 78 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 79
Slide 79 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 80
Slide 80 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 81
Slide 81 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 82
Slide 82 text
@MOLSJEROEN
findNavController().navigate(R.id.action_welcomeFragment_to_loginFragment)
NAVIGATION: IN FEATURE
Slide 83
Slide 83 text
@MOLSJEROEN
NAVIGATION: IN FEATURE
Slide 84
Slide 84 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
startActivity(Intent(activity, DashboardActivity::class.java))
Slide 85
Slide 85 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
startActivity(Intent("action.dashboard.open"))
Slide 86
Slide 86 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
Slide 87
Slide 87 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
Slide 88
Slide 88 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
String duplication of action
In depth knowledge of intent creation
Other apps could also handle action
@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())
Slide 91
Slide 91 text
@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())
Slide 92
Slide 92 text
@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"))
Slide 93
Slide 93 text
@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"))
Slide 94
Slide 94 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
Slide 95
Slide 95 text
@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)
}
Slide 96
Slide 96 text
@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)
}
Slide 97
Slide 97 text
@MOLSJEROEN
NAVIGATION: BETWEEN FEATURES
Implicit intents
Actions module
Abstract away feature parameters
Restrict to current package
@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
@MOLSJEROEN
PULL CODE UP
APP
FEATURE
FEATURE
FEATURE
FUTURE FEATURE
FUTURE FEATURE
FUTURE FEATURE
OLD APP
LIB LIBRARY FUTURE LIBRARY
Pull modules up
Slide 119
Slide 119 text
@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
@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
@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
Slide 125
Slide 125 text
WHEN PLANNING IS LONG THEN IT’S
WRONG, IF IT’S TIGHT THEN IT’S RIGHT
Elon musk
Slide 126
Slide 126 text
LESSONS LEARNED
Slide 127
Slide 127 text
@MOLSJEROEN
CONFIGURE MODULES
Adding a new module must be easy
Maintaining module configurations must be easy
@MOLSJEROEN
ORGANIZE SETTINGS.GRADLE
include ':app', ':features:login', ':features:dashboard', ':features:sharing', ':libraries:ui-
components', ':libraries:actions'
Slide 136
Slide 136 text
@MOLSJEROEN
ORGANIZE SETTINGS.GRADLE
include ':app'
include ':features:login'
include ':features:dashboard'
include ':features:sharing'
include ':libraries:ui-components'
include ':libraries:actions'
Slide 137
Slide 137 text
@MOLSJEROEN
ORGANIZE SETTINGS.GRADLE
include ':app'
include ':features:login'
include ':features:dashboard'
include ':features:sharing'
include ':libraries:ui-components'
include ':libraries:actions'
Slide 138
Slide 138 text
@MOLSJEROEN
MODULE INTERNALS
Slide 139
Slide 139 text
@MOLSJEROEN
MODULE INTERNALS - ORGANIZATION
Slide 140
Slide 140 text
@MOLSJEROEN
MODULE INTERNALS - ORGANIZATION
Slide 141
Slide 141 text
@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
@MOLSJEROEN
SPEED UP BUILDS
dependencies {
// Avoid doing this
api project(':libraries:ui-components')
// Do this instead
implementation project(':libraries:ui-components')
}
Slide 152
Slide 152 text
@MOLSJEROEN
API VS IMPLEMENTATION DEPENDENCY
APP
Slide 153
Slide 153 text
@MOLSJEROEN
API DEPENDENCY
APP
Slide 154
Slide 154 text
@MOLSJEROEN
API DEPENDENCY
APP
Slide 155
Slide 155 text
@MOLSJEROEN
API DEPENDENCY
APP
Slide 156
Slide 156 text
@MOLSJEROEN
IMPLEMENTATION DEPENDENCY
APP
Slide 157
Slide 157 text
WRAP UP
Slide 158
Slide 158 text
THERE ARE NO BIG PROBLEMS, THERE
ARE JUST A LOT OF LITTLE PROBLEMS.
Henry Ford
Slide 159
Slide 159 text
@MOLSJEROEN
MODULARIZATION HAS TONS OF BENEFITS
APP FEATURES LIBRARIES
NAVIGATION WITHIN AND BETWEEN FEATURES
PUSH CODE DOWN
SIMPLIFY ADDING MODULES