Clean Architecture və Repository pattern
Clean Architecture kodu asılılıq istiqaməti sərt qaydalara tabe olan qatlara bölür:
- Presentation (UI + ViewModel) — istifadəçiyə görünən qat
- Domain (use case-lər, biznes modelləri, repository interfeysləri) — framework-suz, təmiz Kotlin
- Data (repository implementasiyaları, API/DB mənbələri, DTO-lar) — xarici dünya ilə iş
Asılılıq qaydası: oxlar həmişə içəri baxır — Data və Presentation Domain-dən asılıdır, Domain heç nədən asılı deyil. Bu, dependency inversion ilə mümkün olur: interfeys domain-də, implementasiya data-da.
Repository pattern — data mənbələrini (şəbəkə, DB, cache) tək fasad arxasında gizlədir: ViewModel "balансı ver" deyir, haradan gəldiyini bilmir. Offline-first strategiyanın qərar nöqtəsi məhz repository-dir.
Hər qatın öz modeli olur: AccountDto (data) → Account (domain) → AccountUiModel (presentation), aralarında mapper-lər.
// DOMAIN — təmiz Kotlin, Android importu yoxdur
data class Account(val id: String, val balance: BigDecimal)
interface AccountRepository {
suspend fun getAccounts(): List<Account>
fun observeAccounts(): Flow<List<Account>>
}
class GetAccountsUseCase(private val repo: AccountRepository) {
suspend operator fun invoke(): List<Account> =
repo.getAccounts().sortedByDescending { it.balance }
}
// DATA — Retrofit/Room burada yaşayır
class AccountRepositoryImpl @Inject constructor(
private val api: AccountApi,
private val dao: AccountDao
) : AccountRepository {
override suspend fun getAccounts(): List<Account> {
val remote = api.fetchAccounts() // List<AccountDto>
dao.upsertAll(remote.map { it.toEntity() })
return dao.getAll().map { it.toDomain() } // DB — single source of truth
}
override fun observeAccounts() = dao.observeAll().map { list -> list.map { it.toDomain() } }
}Qatların kodda görünüşü: interfeys domain-də, implementasiya data-da
"Use case-lər həmişə lazımdır?" mübahisəsinə hazır ol. Dogmatik cavab yox, balans gözlənilir: sadə CRUD ekranında ViewModel → Repository birbaşa əlaqəsi qəbulediləndir; use case o zaman dəyər verir ki, (1) məntiq bir neçə ViewModel-də təkrarlanır, (2) əməliyyat çoxmərhələlidir (köçürmə: limit yoxla → komissiya hesabla → təsdiq et), (3) domain qaydaları UI-dan asılı olmadan test olunmalıdır. Bank tətbiqində köçürmə axını use case üçün ideal nümunədir.
🛠 Tapşırıq
Bir feature-i üç qata böl: AccountDto (data) → Account (domain) → AccountUiModel (presentation) və aralarına mapper yaz.
- Domain faylında *heç bir `android.` və ya Retrofit importu** olmamalıdır — yoxla.
- Repository interfeysini domain-ə, implementasiyanı data-ya qoy.
- Bir use case yaz (məs.
GetAccountsUseCase) və onun həqiqətən dəyər verib-vermədiyini bir cümlə ilə əsaslandır.
Bitdi sayılır: "use case həmişə lazımdırmı" sualına balanslı cavabın hazırdırsa.
📚 Mənbələr və sənədlər
- Android arxitektura bələdçisi — domain layerrəsmideveloper.android.com
- Data layer bələdçisirəsmideveloper.android.com