CreateArticleUseCase.kt

package com.example.realworldkotlinspringbootjdbc.usecase.article

import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.Either.Right
import arrow.core.Validated.Invalid
import arrow.core.Validated.Valid
import arrow.core.left
import arrow.core.right
import com.example.realworldkotlinspringbootjdbc.domain.ArticleRepository
import com.example.realworldkotlinspringbootjdbc.domain.OtherUser
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.UncreatedArticle
import com.example.realworldkotlinspringbootjdbc.usecase.shared_model.CreatedArticleWithAuthor
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service

interface CreateArticleUseCase {
    fun execute(
        currentUser: RegisteredUser,
        title: String?,
        description: String?,
        body: String?,
        tagList: List<String>?
    ): Either<Error, CreatedArticleWithAuthor> = throw NotImplementedError()

    sealed interface Error : MyError {
        data class InvalidArticle(override val errors: List<MyError.ValidationError>) : Error, MyError.ValidationErrors
    }
}

@Service
class CreateArticleUseCaseImpl(
    val articleRepository: ArticleRepository,
) : CreateArticleUseCase {
    override fun execute(
        currentUser: RegisteredUser,
        title: String?,
        description: String?,
        body: String?,
        tagList: List<String>?
    ): Either<CreateArticleUseCase.Error, CreatedArticleWithAuthor> =
        when (
            val uncreatedArticle =
                UncreatedArticle.new(title, description, body, tagList, currentUser.userId)
        ) {
            /**
             * バリデーションエラー
             */
            is Invalid -> CreateArticleUseCase.Error.InvalidArticle(uncreatedArticle.value).left()
            /**
             * 未作成記事 -> 保存 -> 作成済み記事with著者情報
             */
            is Valid -> when (val createdArticle = articleRepository.create(uncreatedArticle.value)) {
                is Left -> { TODO("成功する想定なため、この分岐に入ることはない。こういう時にUnexpected?") }
                is Right -> CreatedArticleWithAuthor(
                    article = createdArticle.value,
                    author = OtherUser.newWithoutValidation(
                        userId = currentUser.userId,
                        username = currentUser.username,
                        bio = currentUser.bio,
                        image = currentUser.image,
                        following = false,
                    )
                ).right()
            }
        }
}