MAD Skills 앱 아키텍쳐 - Domain 레이어

Architecture: The domain layer - MAD Skills (영상, 문서)

💡개념이 헷갈리거나 제가 잘 모르거나 많은 사람들이 잘 모를 것 같은 것 위주로 정리

Domain 레이어

레이어드 구조

  • UI Layer
  • Domain Layer
  • Data Layer

domain-layer

역할

복잡한 비즈니스 로직이나 여러 ViewModel에서 재사용되는 비즈니스 로직의 캡슐화를 담당한다.

UseCase

역할

Domain 레이어의 비지니스 로직을 구현한다.

하나의 기능 당 하나의 UseCase

이점

  • 여러 ViewModel에서 UseCase를 재사용 하므로 코드중복을 방지한다.
  • 도메인 로직만 분리했기 때문에 테스트 가능성을 높인다.
  • UseCase당 하나의 기능만 담당시켜 SRP를 만족시킨다.

이름 규칙

현재시제 동사 + 명사/대상(선택사항) + UseCase usecase-naming

class FormatDateUseCase

class GetLatestNewsWithAuthorsUseCase

사용하는 의존성

  • 여러 Repository를 사용할 수 있다.
  • 다른 UseCase를 사용할 수 있다.
class GetLatestNewsWithAuthorsUseCase(
  private val newsRepository: NewsRepository,
  private val authorsRepository: AuthorsRepository,
  private val formatDateUseCase: FormatDateUseCase
) { /* ... */ }

usecase-dependencies

클린 아키텍쳐를 적용하기

클린 아키텍쳐를 적용한다면 Data 레이어가 Domain 레이어를 참조하는 그림이어야 한다. NewsRepository, AuthorsRepository는 인터페이스로써 Domain 레이어에 위치한다. Repository 인터페이스의 구현체(NewsRepositoryImpl)는 Data 레이어에서 정의한다.

수명주기

UseCase는 고유의 수명주기를 갖지 않으며 UseCase를 사용하는 사용자 클래스의 수명주기에 의존한다.

백그라운드 처리

복잡한 비지리스 로직은 캐싱을 유도하기 위해 Data 레이어에서 이루어질 수 있다. 그렇지 않은 경우 UseCase에서 백그라운드 처리한다.

class GetLatestNewsWithAuthorsUseCase(
    private val newsRepository: NewsRepository,
    private val authorsRepository: AuthorsRepository,
    private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
) {
    suspend operator fun invoke(): List<ArticleWithAuthor> =
        withContext(defaultDispatcher) {
            val news = newsRepository.fetchLatestNews()
            val result: MutableList<ArticleWithAuthor> = mutableListOf()
            // This is not parallelized, the use case is linearly slow.
            for (article in news) {
                // The repository exposes suspend functions
                val author = authorsRepository.getAuthor(article.authorId)
                result.add(ArticleWithAuthor(article, author))
            }
            result
        }
}

vs Util 클래스

UseCase에 존재할 수 있는 로직이 Util 클래스의 정적메소드에 포함되는 경우도 있다. Util 클래스는 찾기 어렵고 기능도 발견하기 어렵기 때문에 권장되지 않는다.