Clean Architecture Setup for Android App

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Clean Architecture Setup for Android App
Complex
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Setting up Clean Architecture for Android Applications

An Android project without clear architecture looks predictable: an Activity with 800 lines, Retrofit interface called directly from onClick, Room DAO returning LiveData<List<User>> straight into Fragment. It works until the first requirement: "add caching", "write tests", "extract a shared module for wear OS". Then it becomes clear that everything is tightly coupled.

Clean Architecture solves this through dependency inversion: inner layers don't know about outer ones. Retrofit and Room can be replaced without changing business logic.

Three Layers and Their Boundaries

Domain — the core. Pure Kotlin without Android imports. Here are Entity models, Repository interfaces, UseCase classes. This module compiles to a JVM library and tests without an emulator.

Data — repository implementations. Retrofit DTO, Room Entity, DTO → Domain mapping. UserRepositoryImpl implements UserRepository from Domain and knows about both data sources:

class UserRepositoryImpl @Inject constructor(
    private val api: UserApi,
    private val dao: UserDao,
    private val mapper: UserMapper
) : UserRepository {

    override fun getUser(id: String): Flow<User> = flow {
        dao.getUser(id)?.let { emit(mapper.fromEntity(it)) }
        try {
            val remote = api.getUser(id)
            dao.upsert(mapper.toEntity(remote))
            emit(mapper.fromDto(remote))
        } catch (e: HttpException) {
            if (dao.getUser(id) == null) throw e
        }
    }
}

Strategy: emit cache first, refresh from server in parallel. If network fails but cache exists — user sees no error.

Presentation — ViewModel, UI (Compose or XML). Depends only on Domain: calls UseCase, gets Flow, transforms into UI state. Doesn't know where data comes from — Room or Retrofit.

UseCase: When Needed, When Not

UseCase is justified when:

  • Orchestrates multiple repositories
  • Contains non-trivial business rules
  • Reused in multiple ViewModels

GetUserUseCase that only does return userRepository.getUser(id) is an extra layer. If ViewModel works with one repository without logic — inject the repository directly.

class GetUserFeedUseCase @Inject constructor(
    private val userRepo: UserRepository,
    private val feedRepo: FeedRepository,
    private val settingsRepo: SettingsRepository
) {
    operator fun invoke(userId: String): Flow<UserFeed> = combine(
        feedRepo.getFeed(userId),
        settingsRepo.getContentFilters()
    ) { feed, filters ->
        feed.filter { filters.allows(it) }
    }
}

That's a real UseCase: combines three sources, applies filtering.

Multi-module: When Monolith Hinders

For a small application, three packages in one module is enough. For a large project (5+ features, multiple teams), transition to multi-module:

:core:domain
:core:data
:feature:profile:domain (optional)
:feature:profile:presentation
:feature:feed:presentation
:app

Multi-module speeds up incremental builds: a change in :feature:profile doesn't rebuild :feature:feed. Gradle api vs implementation between modules is a separate configuration topic.

Hilt + Clean Architecture

Hilt generates a Dagger graph from annotations. @HiltAndroidApp on Application, @AndroidEntryPoint on Activity/Fragment, @HiltViewModel on ViewModel. Bindings between Domain interfaces and Data implementations:

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    @Singleton
    abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}

Scope errors are detected at compile time, not runtime.

Testing by Layers

Layer Tools Android Dependency
Domain UseCase JUnit 5 + Mockk No
Data Repository JUnit 5 + Mockk + MockWebServer No (minimal with Room)
ViewModel Turbine + Coroutines Test No
UI Espresso / Compose UI Test Yes (emulator/device)

Most tests run on JVM — fast and cheap.

Common Implementation Mistakes

Domain models with @Entity or @SerialName. This is a Data layer leak into Domain. Separate DTO, separate mapper.

UseCase with Context. Context is an Android dependency. UseCase in Domain shouldn't know about it. For string resources — use abstraction StringProvider in Domain with implementation in Presentation.

Flow in Domain with Android types. LiveData in Domain violates boundaries. Only kotlinx.coroutines.flow.Flow.

What We Do During Setup

Design modular structure for project size. Configure Hilt with proper scopes. Implement first feature module as example: UseCase + Repository + ViewModel + tests for all layers. Write Gradle convention plugins for uniform configuration across modules.

Timeline

Setup from scratch (single-module project): 3–5 days. Multi-module from scratch: 1–2 weeks. Migration of existing monolith: 3–8 weeks depending on scope. Cost calculated after audit.