๐Ÿ›๏ธ Android Architecture and Clean Code

Modularization Strategies for Large Android Apps

Once you've decided to modularize, the harder question is how to slice the app. By layer, by feature, or a hybrid? With api/impl splits? This guide compares the strategies used in large Android codebases and shows how to migrate a monolith without stopping feature work.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 3 min read ๐ŸŽฏ Advanced
On this page
  1. The main strategies
  2. The api/impl split
  3. Dependency rules worth enforcing
  4. Migrating a monolith step by step
  5. Dynamic feature modules
  6. Measuring success
  7. Examples
  8. Frequently asked questions
  9. Conclusion

Two teams modularize the same 200-screen app. One splits by layer and ends up with a :data module that every change touches. The other splits by feature and gets independent teams and fast builds. The strategy you choose shapes your build times and your team's daily experience for years.

The main strategies

Layer-based versus feature-based versus hybrid modularization Layer-based: three horizontal modules, ui, domain and data, each containing all features. Feature-based: vertical modules per feature, each containing its own layers. Hybrid: vertical feature modules on top of shared horizontal core modules. By layer :ui (all features) :domain (all features) :data (all features) By feature home search cart Hybrid (common) home search cart :core:* (data, ui, model)
Most large Android apps converge on the hybrid: vertical features over horizontal core modules.
StrategyProsConsFits
By layerSimple; enforces layeringEvery feature change touches every layer module; poor build parallelism; no team ownershipSmall apps wanting layer enforcement
By featureIndependent features; ownership; good cachingShared code needs a home; risk of duplicationApps with clear product areas
HybridFeature independence plus shared coreRequires discipline about what goes in coreMost medium and large apps
api/impl splitConsumers depend on a small API module; impl changes don't recompile consumersDoubles module count for split featuresVery large apps, cross-feature contracts

The api/impl split

Module layoutText
:feature:payments:api     โ† interfaces + models other features may use (tiny, stable)
:feature:payments:impl    โ† UI, ViewModels, repositories (changes often)

:feature:checkout  โ†’ depends on :feature:payments:api only
:app               โ†’ depends on :feature:payments:impl (to bind implementations via DI)
feature/payments/api/PaymentLauncher.ktKotlin
interface PaymentLauncher {
    fun paymentRoute(orderId: String): NavKey      // checkout navigates without knowing the impl
}

Because checkout sees only the small API module, editing payment screens doesn't recompile checkout. That pays off in large codebases.

Dependency rules worth enforcing

  • Feature modules don't depend on other feature modules, only on their :api modules.
  • Core modules never depend on feature modules.
  • :core:model and :core:domain are pure Kotlin (no Android dependencies).
  • Only :app knows about every implementation.

Enforce these in CI with a dependency-analysis plugin or a custom Gradle check, not by convention alone.

Migrating a monolith step by step

  1. Measure: record clean and incremental build times and the main pain points.
  2. Set up build logic: a version catalog and convention plugins.
  3. Extract leaf core modules: model, design system, network and database. These have few dependencies on the rest.
  4. Extract one well-bounded feature, such as settings, to prove the pattern, including navigation and DI.
  5. Continue feature by feature, prioritising frequently changed or team-owned areas.
  6. Re-measure and adjust granularity.
Keep shipping

Move code in small, behaviour-preserving PRs. Avoid long-lived modularization branches, which conflict with ongoing feature work.

Dynamic feature modules

Play Feature Delivery can deliver modules on demand or conditionally, which reduces initial download size for large, rarely used features (an AR viewer, an advanced editor). They add complexity: different navigation, testing and dependency direction. Reserve them for genuinely large, optional features.

Measuring success

  • Incremental build time for typical changes.
  • CI time and build-cache hit rate.
  • Number of modules touched per PR (it should drop).
  • Developer feedback on sync time and navigation.

Examples

E-commerce app

Hybrid: catalogue, product, cart, checkout and account features over core data, design system and analytics, with an api/impl split for payments.

Banking app

Strict feature isolation, security-sensitive core modules owned by a platform team, and dependency rules in CI.

Media app

Player as a core module, with a dynamic feature for offline downloads in markets where it's used.

Frequently asked questions

How big should a feature module be?

Roughly one product area owned by one team: a few screens with their ViewModels and data. Split further only for build or ownership reasons.

Where does shared UI go?

Generic components go in :core:designsystem. Feature-specific but shared UI may need a small shared module, but avoid a dumping-ground "common" module.

Does modularization help app size?

Not by itself. R8 already removes unused code. Dynamic features can reduce initial download size.

Conclusion

For most large Android apps, the winning strategy is hybrid: vertical feature modules over a disciplined set of core modules, with api/impl splits where cross-feature contracts are hot. Migrate incrementally, enforce dependency rules automatically, and measure the outcome.

Next step: apply design principles inside those modules with SOLID Principles in Android Development.

Official references

๐Ÿ“ฑ Apps built by Yunok Tech

Real Android apps on Google Play, free to download.

See all our apps โ†’

Ready to build real Android applications?

Architecture makes the most sense when you apply it to a real project and see why each boundary exists. Learn how maintainable Android applications are designed by building one.

Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.

Explore the Android course and take your next step as an Android developer.

Share: LinkedIn X / Twitter WhatsApp