Android Clean Architecture Explained (With Practical Examples)
February 26, 2026
Modern Android apps quickly become complex. Without a proper structure, Activities and ViewModels turn into massive, unmaintainable classes.
This is where Clean Architecture becomes a game changer.
In this guide, you’ll learn:
✔ What Clean Architecture is
✔ Why it matters in Android development
✔ Layered structure explained
✔ Dependency rules
✔ Practical Kotlin examples
✔ Common mistakes to avoid

✅ What is Clean Architecture?
Clean Architecture is a software design pattern that separates an application into distinct layers, ensuring:
- Clear separation of concerns
- Framework independence
- High testability
- Scalability & maintainability
Core philosophy:
Business logic should not depend on UI, databases, or frameworks.
✅ Why Clean Architecture is Important in Android
Without architecture:
❌ UI & logic tightly coupled
❌ Hard to test
❌ Difficult to scale
❌ Fragile codebase
With Clean Architecture:
✅ Testable business rules
✅ Flexible technology choices
✅ Clean & modular code
✅ Easier debugging
✅ The Three Core Layers
Android Clean Architecture typically consists of:
- Presentation Layer
- Domain Layer
- Data Layer
Each layer has a distinct responsibility.
🟢 1. Presentation Layer (UI & State Management)
Responsible for:
✔ Displaying UI
✔ Handling user actions
✔ Managing UI state
Contains:
- Activities / Fragments / Composables
- ViewModels
- UI State classes
Example structure:
presentation/
├── ui/
├── viewmodel/
└── state/
✅ Example ViewModel
class UserViewModel(
private val getUserProfileUseCase: GetUserProfileUseCase
) : ViewModel() { private val _state = MutableStateFlow<UserState>(UserState.Loading)
val state: StateFlow<UserState> = _state fun loadUser(userId: String) {
viewModelScope.launch {
try {
val user = getUserProfileUseCase(userId)
_state.value = UserState.Success(user)
} catch (e: Exception) {
_state.value = UserState.Error(e.message ?: "Unknown error")
}
}
}
}
✅ UI State Representation
sealed class UserState {
object Loading : UserState()
data class Success(val user: User) : UserState()
data class Error(val message: String) : UserState()
}
👉 Notice: No API / DB logic here.
🟡 2. Domain Layer (Core Business Logic)
The most important layer.
Responsible for:
✔ Business rules
✔ Use cases
✔ Core models
✔ Framework independence
Contains:
- UseCases
- Domain Models
- Repository Interfaces
Example structure:
domain/
├── model/
├── repository/
└── usecase/
✅ Domain Model Example
data class User(
val id: String,
val name: String,
val email: String
)
Pure Kotlin → No Retrofit / Room / Android.
✅ Use Case Example
class GetUserProfileUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(userId: String): User {
return repository.getUser(userId)
}
}
UseCases encapsulate single business actions.
✅ Repository Interface
interface UserRepository {
suspend fun getUser(userId: String): User
}
👉 Domain defines contract, not implementation.
🔵 3. Data Layer (Implementation Details)
Responsible for:
✔ API calls
✔ Database operations
✔ Mapping models
✔ Repository implementations
Example structure:
data/
├── remote/
├── local/
├── mapper/
└── repository/
✅ API DTO Example
data class UserDto(
val id: String,
val full_name: String,
val email_address: String
)
✅ Mapper Example
fun UserDto.toDomain(): User {
return User(
id = id,
name = full_name,
email = email_address
)
}
DTO → Domain Model conversion is critical.
✅ Repository Implementation
class UserRepositoryImpl(
private val api: UserApi
) : UserRepository { override suspend fun getUser(userId: String): User {
return api.getUser(userId).toDomain()
}
}
✅ Dependency Flow (Very Important)
Data flow:
UI → ViewModel → UseCase → Repository → Data Sources
Dependency rule:
Presentation → Domain → Data
Never:
Domain → Android ❌
Domain → Retrofit ❌
✅ Key Benefits of Clean Architecture
✅ 1. High Testability
Domain layer = Pure Kotlin → Easy unit tests.
✅ 2. Scalability
Project grows → Layers remain manageable.
✅ 3. Replaceable Technologies
Swap:
✔ Retrofit → Ktor
✔ Room → SQLDelight
✔ XML → Compose
Without rewriting business logic.
✅ 4. Cleaner Codebase
Responsibilities are predictable & isolated.
✅ Common Mistakes Developers Make
Avoid these:
❌ Business logic inside ViewModel
❌ API models used as domain models
❌ Skipping UseCases in complex apps
❌ Domain layer depending on frameworks
✅ When Should You Use Clean Architecture?
Best for:
✅ Medium & Large apps
✅ Long-term projects
✅ Team-based development
✅ Complex business logic
Overkill for:
✔ Tiny apps
✔ Prototypes / POCs
✅ Mental Model to Remember
Think of layers like:
🏛 Presentation = UI layer
🧠 Domain = Brain / logic
🔌 Data = External systems