Modularisation
Modularisation
1. What is Android Modularisation?
Android modularisation means dividing one large Android application into multiple smaller Gradle modules.
Instead of keeping everything inside the app module, code is separated based on features and common responsibilities.
Example
:app
:feature:login
:feature:payments
:feature:accounts
:core:network
:core:database
:core:designsystemSimple meaning
flowchart TD
A["Large application"] --> B["Divide into smaller independent modules"]2. Why Do We Use Modularisation?
Modularisation helps to:
- Organise code properly
- Improve incremental build time
- Make testing easier
- Allow teams to work independently
- Reuse common components
- Reduce feature dependency
- Maintain large applications easily
It is mainly useful for medium and large Android applications.
3. How Does Modularisation Work?
Each module has a specific responsibility.
flowchart TD
A["App Module"] --> L["Feature: Login"]
A --> P["Feature: Payments"]
A --> AC["Feature: Accounts"]
L --> N["Core: Network"]
L --> D["Core: Database"]
L --> DS["Core: Design System"]
P --> N
P --> D
P --> DS
AC --> N
AC --> D
AC --> DS
The recommended dependency direction is:
flowchart LR
A["App Module"] --> B["Feature Modules"]
B --> C["Core Modules"]
Core modules should not depend on feature modules.
4. Classification of Modules
4.1 App Module
The app module is the main entry point of the application.
It usually contains:
Applicationclass- Main activity
- Top-level navigation
- Application manifest
- Dependency injection setup
- Feature module integration
flowchart TD
A["App Module"] --> B["MainActivity"]
A --> C["Application"]
A --> D["AppNavigation"]
The app module should connect features, not contain all business logic.
4.2 Feature Modules
A feature module contains code related to one business feature.
Examples:
:feature:login
:feature:payments
:feature:profile
:feature:transactionsA feature module may contain:
Screen or Composable
ViewModel
UI state
Use case
Repository interface
Feature navigation contract
Example:
flowchart TD
A["Feature: Payments"] --> B["PaymentScreen"]
A --> C["PaymentViewModel"]
A --> D["PaymentUiState"]
A --> E["MakePaymentUseCase"]
A --> F["PaymentRepository"]
4.3 Core Modules
Core modules contain shared technical functionality used by multiple features.
| Core module | Responsibility |
|---|---|
core:network | Retrofit, OkHttp, interceptors |
core:database | Room database, DAO, entities |
core:designsystem | Buttons, text fields, themes |
core:common | Utilities, result wrappers |
core:model | Shared models |
core:analytics | Analytics tracking |
core:testing | Shared test utilities |
Core modules should contain reusable code, not feature-specific business logic.
4.4 Reusable Component Module
Reusable UI components are normally placed inside core:designsystem.
Examples:
- Primary button
- Loading indicator
- Common toolbar
- Error dialog
- Common text field
@Composable
fun AppPrimaryButton(
text: String,
onClick: () -> Unit
) {
Button(onClick = onClick) {
Text(text)
}
}This button can be used by login, payment and profile features.
5. Dependency Graph
A dependency graph shows how modules depend on each other.
flowchart TD
A["App"]
A --> L["Feature: Login"]
A --> P["Feature: Payments"]
A --> AC["Feature: Accounts"]
L --> N["Core: Network"]
L --> D["Core: Database"]
L --> DS["Core: Design System"]
P --> N
P --> D
P --> DS
AC --> N
AC --> D
AC --> DS
Important rules
Correct:
app -> feature
feature -> core
core -> common utilityIncorrect:
core -> feature
feature:login -> feature:paymentAvoid circular dependencies:
feature:login -> feature:profile
feature:profile -> feature:login6. Feature-First Structure
Feature-first structure organises code based on business features.
feature
├── login
├── payments
├── accounts
└── profileEach feature can contain its own layers:
feature:payments
├── presentation
├── domain
└── dataThis is commonly used in large modular Android applications.
7. Feature-First vs Layer-First
Feature-first
feature:login
feature:payments
feature:accountsLayer-first
presentation
domain
dataComparison
| Feature-first | Layer-first |
|---|---|
| Organised by business feature | Organised by technical layer |
| Feature code stays together | Feature code is spread across layers |
| Better for large teams | Suitable for smaller projects |
| Easier feature ownership | Easier strict layer separation |
| Common in modular apps | Common in simple Clean Architecture |
Recommended approach
Use feature-first modules with layers inside each feature.
feature:payments
├── presentation
├── domain
└── data8. Monolithic vs Modular App
| Monolithic app | Modular app |
|---|---|
Most code is inside app | Code is divided into modules |
| Features are tightly connected | Features are isolated |
| Harder to maintain | Easier to maintain |
| Slower large-project builds | Better incremental builds |
| Difficult team ownership | Clear module ownership |
| Suitable for small apps | Suitable for medium and large apps |
9. implementation vs api
implementation
implementation(project(":core:network"))The dependency remains internal to the module.
Use it by default.
api
api(project(":core:model"))The dependency is also exposed to other modules.
implementation | api |
|---|---|
| Keeps dependency internal | Exposes dependency |
| Better isolation | More visibility |
| Preferred by default | Use only when required |
| Reduces unnecessary recompilation | Can increase recompilation |
10. Banking App Example
Banking App
│
├── app
│ ├── MainActivity
│ └── AppNavigation
│
├── feature
│ ├── login
│ ├── accounts
│ ├── payments
│ └── transactions
│
└── core
├── network
├── database
├── designsystem
├── common
└── modelExample payment flow:
PaymentScreen
|
v
PaymentViewModel
|
v
MakePaymentUseCase
|
v
PaymentRepository
|
v
API / Database11. Common Mistakes
1. Creating too many small modules
Bad:
:button
:text
:login-title
:login-buttonCreate modules around meaningful features or responsibilities.
2. Creating a huge common module
Do not place networking, database, UI and business logic inside one common module.
Use focused modules:
core:network
core:database
core:designsystem3. Feature-to-feature dependency
Avoid:
feature:payments -> feature:accountsUse navigation callbacks, shared interfaces or contracts.
4. Circular dependency
Avoid:
A -> B -> A5. Using api everywhere
Prefer implementation unless the dependency must be exposed.
6. Moving code to core too early
Move code to core only when it is genuinely reused by multiple features.
7. Putting business logic in the app module
The app module should connect features. Business logic should remain inside feature modules.
12. Advantages and Limitations
Advantages
* Better code organisation
* Better team ownership
* Reusable components
* Easier testing
* Better incremental builds
* Clear feature boundaries
* Easier maintenance
Limitations
* More Gradle files
* Initial setup takes time
* Navigation becomes slightly complex
* Dependency management needs discipline
* Too many modules can increase complexity
13. Important Interview Questions and Answers
Q1. What is Android modularisation?
Android modularisation means dividing a large application into smaller Gradle modules based on features and shared responsibilities.
Q2. What are the main types of modules?
The main modules are:
* App module
* Feature modules
* Core modules
* Reusable component modules
Q3. What is the responsibility of the app module?
The app module contains application-level setup, main activity, top-level navigation, manifest and feature integration.
Q4. What is a feature module?
A feature module contains code related to one business feature, such as login, payments or profile.
Q5. What is a core module?
A core module contains reusable technical functionality such as networking, database, analytics, common models or UI components.
Q6. What is the recommended dependency direction?
App -> Features -> CoreCore modules should not depend on feature modules.
Q7. Why should feature modules not directly depend on each other?
Direct dependencies increase coupling and may create circular dependencies. Features should communicate through contracts, callbacks or shared interfaces.
Q8. Feature-first or layer-first—which is better?
Feature-first is generally better for large modular applications because all code related to one feature stays together.
A practical structure is:
Feature module
├── presentation
├── domain
└── dataQ9. What is the difference between implementation and api?
implementation keeps a dependency internal, while api exposes it to other modules. implementation should be preferred.
Q10. Does modularisation always improve build time?
No. It can improve incremental builds, but poor dependency graphs, too many modules and public API changes can still slow down builds.
Q11. How do you decide whether code belongs in core?
Move code to core only when it is reused by multiple features and represents a common technical responsibility.
Q12. How would you modularise a banking app?
I would create feature modules for login, accounts, payments and transactions. Shared networking, database, models and UI components would be placed in core modules. The app module would handle top-level navigation and feature integration.
14. Interview-Friendly Final Answer
Android modularisation means dividing a large application into smaller Gradle modules. The app module handles application-level setup and navigation, feature modules contain business features such as login or payments, and core modules contain shared functionality such as networking, database and reusable UI components. The recommended dependency direction is app to features to core. Modularisation improves maintainability, testing, team ownership, code reuse and incremental build performance, but over-modularisation and circular dependencies should be avoided.