On This Page
1Overview2Module Architecture
3iOS Swift Package Manager Modularization4Team Topology Alignment
5Anti-Patterns to Avoid6References

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

Build Performance Impact

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

  1. Google — Android Modularization Guide. developer.android.com/topic/modularization
  2. Skelton, Matthew and Pais, Manuel — Team Topologies. IT Revolution Press, 2019.
  3. Google — Build Performance Guide. developer.android.com/build/optimize-your-build
  4. Gradle — Build Caching. docs.gradle.org/current/userguide/build_cache.html
Mobile Engineering Reference
← Mobile Development