Repository·UseCase — 데이터 단일 출처와 비즈니스 캡슐화
데이터 계층의 얼굴이 Repository, 도메인 계층의 일꾼이 UseCase다. Repository는 데이터가 어디서 오는지(API·DB·캐시)를 감추고 단일 출처가 되며, UseCase는 하나의 비즈니스 동작을 캡슐화해 재사용·테스트를 쉽게 한다. 이 둘이 ViewModel과 데이터 소스 사이를 잇는다. (출처: Android — Data layer / Domain layer.)
Repository — 데이터의 단일 출처(SSOT)
Repository는 여러 데이터 소스(원격 API·로컬 DB·인메모리 캐시)를 조율해 상위 계층엔 하나의 일관된 인터페이스만 보여준다. ViewModel은 데이터가 캐시에서 왔는지 네트워크에서 왔는지 모른다 — 그저 Repository에 요청한다.
interface MemberRepository {
fun observeMembers(): Flow<List<Member>> // DB를 구독 — 변경 시 자동 방출
suspend fun refresh() // 네트워크에서 받아 DB 갱신
}
class MemberRepositoryImpl(
private val api: MemberApi,
private val dao: MemberDao,
) : MemberRepository {
override fun observeMembers() = dao.observeAll().map { it.toDomain() }
override suspend fun refresh() {
val remote = api.getMembers() // 네트워크
dao.upsertAll(remote.toEntities()) // 로컬 DB 갱신 → observe가 자동 방출
}
}
오프라인 우선 — DB를 진실의 원천으로
현대 안드로이드의 권장은 오프라인 우선(offline-first) — 로컬 DB(Room)를 SSOT로 두고, UI는 DB를 구독하며, 네트워크는 DB를 갱신할 뿐이다. 그래서 네트워크가 끊겨도 앱이 동작하고, 네트워크 갱신이 DB를 통해 UI에 반영된다(단방향). Flow로 DB를 구독하면 데이터 변경이 화면까지 자동으로 흐른다.
UseCase — 하나의 비즈니스 동작
UseCase(Interactor)는 하나의 비즈니스 규칙·동작을 캡슐화한다 — 여러 Repository를 조합하거나 복잡한 규칙을 담아, ViewModel을 얇게 유지하고 로직을 재사용·테스트하게 한다. 코틀린에선 *operator fun invoke*로 함수처럼 호출하는 관용구를 쓴다.
class GetVisibleMembersUseCase(
private val repo: MemberRepository,
private val settings: SettingsRepository,
) {
operator fun invoke(): Flow<List<Member>> =
combine(repo.observeMembers(), settings.showHidden()) { members, showHidden ->
if (showHidden) members else members.filter { it.visible }
}
}
// ViewModel에서 — 함수처럼 호출
val members = getVisibleMembers() // invoke 덕에 () 로 호출
언제 UseCase를 두나 — 여러 Repository를 합치거나, 같은 로직을 여러 ViewModel이 쓰거나, 규칙이 복잡할 때. 단순 위임뿐이면 생략(ViewModel이 Repo 직접 호출)해도 된다 — 불필요한 패스스루 UseCase는 보일러플레이트다.
결과 표현 — Result로 성공/실패
데이터 호출은 실패할 수 있으니 Result나 sealed 타입으로 성공/실패를 명시해 ViewModel이 처리하게 한다.
suspend fun refresh(): Result<Unit> = runCatching {
dao.upsertAll(api.getMembers().toEntities())
} // ViewModel: .onSuccess{} .onFailure{}
정리 — 감추고(Repository) 캡슐화한다(UseCase)
- Repository: 데이터 소스를 감추는 단일 출처(SSOT). 오프라인 우선으로 DB를 진실의 원천에 두고 Flow로 구독. 네트워크는 DB를 갱신.
- UseCase: 하나의 비즈니스 동작을 캡슐화(여러 Repo 조합·복잡 규칙).
invoke로 함수처럼. 단순 위임이면 생략.
이 둘이 ViewModel을 얇게, 데이터 흐름을 단방향으로 만든다.
한 줄 요약 — Repository=데이터 소스(API·Room) 감추는 단일 출처, 오프라인 우선(DB를 SSOT·Flow 구독·네트워크는 DB 갱신). UseCase=하나의 비즈니스 동작(여러 Repo 조합·
operator invoke), 단순 위임이면 생략. 실패는Result로.
(출처: Android — Data layer / Offline-first / Domain layer / Now in Android.)