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:designsystem

Simple 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:

  • Application class
  • 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:transactions

A 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 moduleResponsibility
core:networkRetrofit, OkHttp, interceptors
core:databaseRoom database, DAO, entities
core:designsystemButtons, text fields, themes
core:commonUtilities, result wrappers
core:modelShared models
core:analyticsAnalytics tracking
core:testingShared 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 utility
Incorrect:

core -> feature
feature:login -> feature:payment

Avoid circular dependencies:

feature:login -> feature:profile
feature:profile -> feature:login


6. Feature-First Structure

Feature-first structure organises code based on business features.

feature
 ├── login
 ├── payments
 ├── accounts
 └── profile

Each feature can contain its own layers:

feature:payments
 ├── presentation
 ├── domain
 └── data

This is commonly used in large modular Android applications.


7. Feature-First vs Layer-First

Feature-first

feature:login
feature:payments
feature:accounts

Layer-first

presentation
domain
data

Comparison

Feature-firstLayer-first
Organised by business featureOrganised by technical layer
Feature code stays togetherFeature code is spread across layers
Better for large teamsSuitable for smaller projects
Easier feature ownershipEasier strict layer separation
Common in modular appsCommon in simple Clean Architecture

Recommended approach

Use feature-first modules with layers inside each feature.

feature:payments
 ├── presentation
 ├── domain
 └── data


8. Monolithic vs Modular App

Monolithic appModular app
Most code is inside appCode is divided into modules
Features are tightly connectedFeatures are isolated
Harder to maintainEasier to maintain
Slower large-project buildsBetter incremental builds
Difficult team ownershipClear module ownership
Suitable for small appsSuitable 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.

implementationapi
Keeps dependency internalExposes dependency
Better isolationMore visibility
Preferred by defaultUse only when required
Reduces unnecessary recompilationCan increase recompilation


10. Banking App Example

Banking App
│
├── app
│   ├── MainActivity
│   └── AppNavigation
│
├── feature
│   ├── login
│   ├── accounts
│   ├── payments
│   └── transactions
│
└── core
    ├── network
    ├── database
    ├── designsystem
    ├── common
    └── model

Example payment flow:

PaymentScreen
      |
      v
PaymentViewModel
      |
      v
MakePaymentUseCase
      |
      v
PaymentRepository
      |
      v
API / Database


11. Common Mistakes

1. Creating too many small modules

Bad:

:button
:text
:login-title
:login-button

Create 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:designsystem

3. Feature-to-feature dependency

Avoid:

feature:payments -> feature:accounts

Use navigation callbacks, shared interfaces or contracts.

4. Circular dependency

Avoid:

A -> B -> A

5. 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 -> Core

Core 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
 └── data


Q9. 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.