Overview
Feature modularization divides the application into independently buildable, testable, and (where applicable) deliverable units aligned with product features and team ownership. The benefits compound over time: build times scale with change scope rather than codebase size, teams develop features in parallel without stepping on each other, and the module dependency graph enforces architectural rules that code reviews cannot.
Module Architecture
Android Gradle Multi-Module Structure
The recommended module graph for a medium-to-large Android application:
:app (application module — entry point, DI graph assembly)
:feature:account
:feature:transfer
:feature:onboarding
:feature:settings
:core:domain (shared domain models, repository interfaces, base Use Cases)
:core:network (API client infrastructure, interceptors, token refresh)
:core:database (Room database, shared DAOs)
:core:ui (shared design system components, theme)
:core:testing (test utilities, fake implementations)
Rules: feature modules depend only on :core modules, never on other feature modules. If two features need to share data, the shared data lives in :core:domain. If two features need to navigate to each other, navigation is mediated through the :app module's navigation graph.
Module Type Classification
- Application modules:
:app — assemble the app, configure DI
- Feature modules:
:feature:* — screen-level UI, ViewModel, feature-specific Use Cases
- Library modules:
:core:* — shared infrastructure, never contain UI
- Dynamic feature modules: Optional — for on-demand feature delivery via Play Feature Delivery
A change to :feature:account triggers rebuild of :feature:account and :app. All other feature modules are cache hits. Without modularization, any change triggers a full rebuild. At 100 engineers making 10 builds per day, the build time saving from modularization is measured in engineering weeks per year.
iOS Swift Package Manager Modularization
The equivalent on iOS uses Swift Package Manager local packages. Each feature is a local Swift package with its own module targets: AccountUI, AccountDomain, AccountData. The main Xcode project imports only the packages it needs. SPM enforces the dependency graph — circular dependencies fail the build.
Team Topology Alignment
Map module ownership to team ownership. The Account team owns :feature:account. The Platform team owns :core:*. Merge conflicts on a module signal a team boundary violation — two teams are modifying the same module, indicating their feature boundary needs redefinition.
Anti-Patterns to Avoid
⚠ 1. Modularization by Layer Instead of Feature
:ui, :domain, :data as top-level modules. The entire UI team modifies :ui. Every feature change requires coordinating across three modules. Build times improve marginally because the entire :ui module rebuilds on any screen change.
Hover to see the fix ↻
↺ Correct Approach
Modularize by feature: :feature:account contains its own UI, domain, and data layers. The account team works entirely within their module. Other teams' build caches are unaffected by account changes.
⚠ 2. Feature Modules Depending on Each Other
:feature:transfer imports :feature:account to reuse the AccountSummary composable. Creates tight coupling between features, breaking independent deployability.
Hover to see the fix ↻
↺ Correct Approach
Move shared UI components to :core:ui. Move shared domain models to :core:domain. Features depend only on core modules.
Flowchart
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%%
flowchart TD
subgraph App[":app — Entry Point"]
ENTRY["Application
DI Graph Assembly
Navigation Host"]
end
subgraph Features[":feature modules — Team Owned"]
FA[":feature:account
Account Team"]
FT[":feature:transfer
Payments Team"]
FO[":feature:onboarding
Growth Team"]
end
subgraph Core[":core modules — Platform Team"]
CD[":core:domain
Shared Domain Models
Repository Interfaces"]
CN[":core:network
API Client Infrastructure
Token Refresh"]
CUI[":core:ui
Design System
Shared Components"]
CDB[":core:database
Room Database"]
CT[":core:testing
Fake Implementations
Test Utilities"]
end
ENTRY --> FA & FT & FO
FA & FT & FO --> CD & CN & CUI
CD --> CDB
CT -.->|"Test dependencies only"| FA & FT & FO
style App fill:#FFEBEE,stroke:#B71C1C
style Features fill:#E3F2FD,stroke:#1565C0
style Core fill:#E8F5E9,stroke:#1B5E20
References
- Google — Android Modularization Guide. developer.android.com/topic/modularization
- Skelton, Matthew and Pais, Manuel — Team Topologies. IT Revolution Press, 2019.
- Google — Build Performance Guide. developer.android.com/build/optimize-your-build
- Gradle — Build Caching. docs.gradle.org/current/userguide/build_cache.html
Mobile Engineering Reference
← Mobile Development