FeedUseCase.kt
package com.example.realworldkotlinspringbootjdbc.usecase.article
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.example.realworldkotlinspringbootjdbc.domain.ArticleRepository
import com.example.realworldkotlinspringbootjdbc.domain.ProfileRepository
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.article.FeedParameters
import com.example.realworldkotlinspringbootjdbc.usecase.shared_model.CreatedArticleWithAuthor
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service
import java.util.SortedSet
interface FeedUseCase {
/**
* 特定のユーザーのフォローしている人のそれぞれの最新記事一覧
*
* @property articles フィルタされた作成済み記事の一覧(最大数: limit)
* @property articlesCount フィルタで引っかかった作成済み記事の数(メタデータ)
*/
data class FeedCreatedArticles(
val articles: SortedSet<CreatedArticleWithAuthor>,
val articlesCount: Int,
)
fun execute(
currentUser: RegisteredUser,
limit: String? = null,
offset: String? = null,
): Either<Error, FeedCreatedArticles> = throw UnsupportedOperationException()
/**
* - フィード用のバリデーションエラー
* - Offset値がフィルタ済み作成済み記事の数を超過エラー
*/
sealed interface Error : MyError {
data class FeedParameterValidationErrors(
override val errors: List<MyError.ValidationError>
) : Error, MyError.ValidationErrors
data class OffsetOverCreatedArticlesCountError(
val feedParameters: FeedParameters,
val articlesCount: Int,
) : Error, MyError.Basic
}
}
@Service
class FeedUseCaseImpl(
val profileRepository: ProfileRepository,
val articleRepository: ArticleRepository,
) : FeedUseCase {
override fun execute(
currentUser: RegisteredUser,
limit: String?,
offset: String?
): Either<FeedUseCase.Error, FeedUseCase.FeedCreatedArticles> {
/**
* フィード用パラメータ
* バリデーションエラー -> 早期return
*/
val feedParameters = FeedParameters.new(limit = limit, offset = offset).fold(
{ return FeedUseCase.Error.FeedParameterValidationErrors(errors = it).left() },
{ it }
)
/**
* フォローしている登録済みユーザー郡
* エラー -> ありえない
*/
val followedUsers = profileRepository.filterFollowedByUser(currentUser.userId).fold(
{ throw UnsupportedOperationException("現在この分岐に入ることは無い") },
{ it }
)
/**
* 指定した人の最新の作成済み記事郡
* エラー -> ありえない
* - 順番: 記事Id(昇順)
*/
val latestArticlesWithAuthor =
articleRepository.latestByAuthors(followedUsers.map { it.userId }.toSet(), currentUser.userId).fold(
{ throw UnsupportedOperationException("現在この分岐に入ることは無い") },
{
it.map { article ->
CreatedArticleWithAuthor(
article = article,
author = followedUsers.find { user -> user.userId == article.authorId }!!
)
}.toSortedSet(compareBy { v -> v.article.id.value })
}
)
/**
* LimitとOffsetで制限
*/
return when (latestArticlesWithAuthor.size < feedParameters.offset) {
/**
* Offset値が全体を超えてしまっている
*/
true -> FeedUseCase.Error.OffsetOverCreatedArticlesCountError(
feedParameters = feedParameters,
articlesCount = latestArticlesWithAuthor.size,
).left()
/**
* Offset値分ずらせる
*/
false -> FeedUseCase.FeedCreatedArticles(
articles = latestArticlesWithAuthor
.toList<CreatedArticleWithAuthor>()
.slice(feedParameters.offset until latestArticlesWithAuthor.size)
.take(feedParameters.limit)
.toSortedSet(compareBy { it.article.id.value }),
articlesCount = latestArticlesWithAuthor.size,
).right()
}
}
}