[Android]忙しい人のためのアーキテクチャガイド~Domain Layer編~

#はじめに
この記事は、シリーズ作です。

https://qiita.com/reo-androider/items/1ca168e290568a578cd6

https://qiita.com/reo-androider/items/18ec55f35c9a4fee66d3

https://qiita.com/reo-androider/items/8a0b7e9644631f44d19e

今回のDomain Layer編は、元々の分量が少ないです。
スクロール数は元と変わらないかもですが、文字数は 1 / 3　くらいにまで縮められたと思います。
#目次
- Domain Layerとは？
- Domain Layerのメリット
- 他の責務との関係
- 呼び出し
- ライフサイクル
- スレッド周り
- Domain Layerの使われ方

##Domain Layerとは？
https://qiita.com/reo-androider/items/1ca168e290568a578cd6#domain-layer
##Domain Layerのメリット
Domain Layerに責務を切り分けるメリットは

- コードの重複を回避
- ViewModelの肥大化を回避
- アプリのテスタビリティが向上
- 大規模なクラスを回避

##他の責務との関係
Domain Layerは、ViewModelとRepositoryの間に入ります。
Domainクラスは、単一の機能に対して責務を持ます。
また、再利用可能なので、あるユースケースが他のユースケースに依存することもあります。

```kotlin:document
class GetLatestNewsWithAuthorsUseCase(
  private val newsRepository: NewsRepository,
  private val authorsRepository: AuthorsRepository
) { /* ... */ }
```

```kotlin:document
class GetLatestNewsWithAuthorsUseCase(
  private val newsRepository: NewsRepository,
  private val authorsRepository: AuthorsRepository,
  private val formatDateUseCase: FormatDateUseCase
) { /* ... */ }
```

##呼び出し
ユースケースは、オーバーロードを用いることで関数であるかのように呼び出すことができます。
Kotlinでオーバーロードを定義するには、`operator`キーワードを用います。

```kotlin:document
class FormatDateUseCase(userRepository: UserRepository) {

    private val formatter = SimpleDateFormat(
        userRepository.getPreferredDateFormat(),
        userRepository.getPreferredLocale()
    )

    operator fun invoke(date: Date): String {
        return formatter.format(date)
    }
}
```

```kotlin
class MyViewModel(formatDateUseCase: FormatDateUseCase) : ViewModel() {
    init {
        val today = Calendar.getInstance()
        val todaysDate = formatDateUseCase(today)
        /* ... */
    }
}
```

##ライフサイクル
ユースケースは、自身のライフサイクルを持たず、呼び出し元のスコープで生存します。
また、可変データを取る設計をしないので（すべきではない）、毎回新しいインスタンスを呼び出します。

##スレッド周り
ユースケースのロジックは、長時間操作の可能性があるので、メインスレッドから安全に呼び出せるようにしないといけません。
キャッシュなど多くのリソースを消費する計算処理は、Data Layerの責務なので混在しないようにしましょう。

```kotlin:document
class MyUseCase(
    private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {

    suspend operator fun invoke(...) = withContext(defaultDispatcher) {
        // Long-running blocking operations happen on a background thread.
    }
}
```

##Domain Layerの使われ方
同じことの繰り返しになりますが、念の為。
###再利用性
ViewModelで同じようなロジックが定義されている場合、ユースケースに集約させることで再利用性と個別のテストを実現します。
また、集約されているので、変更時は一箇所のみで済みます。

###複数のRepositoryが絡むロジック
図の`GetLatestNewsWithAuthorsUseCase`は、二つのRepositoryを用いたロジックになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599059/9b6beccd-b116-1b2b-6cba-0ee53748136b.png)

下記のユースケースが入ると思うと、かなりViewModelがスッキリしますね。

```kotlin
/**
 * This use case fetches the latest news and the associated author.
 */
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
        }
}
```

#おわりに
いかがだったでしょうか。
これにて、全ての項目を読了いたしました。

お疲れ様でした。
#参考
https://developer.android.com/jetpack/guide/domain-layer