arrow-exact icon indicating copy to clipboard operation
arrow-exact copied to clipboard

Feature: Using Exact for data class / class with multiple properties

Open PoisonedYouth opened this issue 1 year ago • 4 comments

I have the following requirement in one of my productive applications using Either for exeption handling.

I have a domain model that consists of multiple properties and it should only be possible to create valid objects. A valid object depends on a validation that includes multiple of the properties. To achieve this with a data class I have an implementation similar to below (simplified for better overview):

sealed interface Topic {
    val id: UUID
    val name: String
    val title: String
    val category: String

    companion object {
        operator fun invoke(id: UUID, name: String, title: String, category: String): Either<Failure, Topic> {
            return TopicModel.create(id, name, title, category)
        }
    }

    private data class TopicModel private constructor(
        override val id: UUID,
        override val name: String,
        override val title: String,
        override val category: String,
    ) : Topic {

        companion object {
            fun create(id: UUID, name: String, title: String, category: String): Either<Failure, Topic> {
                return either {
                    ensure(category.length > 5) {
                        Failure.ValidationFailure("The category must be longer than 5 characters!")
                    }
                    ensure(name.isNotEmpty() || title.isNotEmpty()) {
                        Failure.ValidationFailure("Either name or title must not be empty!")
                    }
                    TopicModel(
                        id = id,
                        name = name,
                        title = title,
                        category = category
                    )
                }
            }
        }
    }
}

The complexity comes from the fact that I don't want to expose the copy - constructor.

For this requirement I can also use Exact but only by providing a kind of DTO object as constructor parameter. This does not feel great.

I used a class instead of a data class because the implementation is simpler.

class TopicDto(
    val id: UUID,
    val name: String,
    val title: String,
    val category: String
)

class Topic private constructor(
    val id: UUID,
    val name: String,
    val title: String,
    val category: String,
) {
    companion object : Exact<TopicDto, Topic> {
        override fun Raise<ExactError>.spec(raw: TopicDto): Topic {
            ensure(raw.category.length > 5)
            ensure(raw.name.isNotEmpty() || raw.title.isNotEmpty())
            return Topic(
                id = raw.id,
                name = raw.name,
                title = raw.title,
                category = raw.category
            )
        }

    }
}

I would like to have a solution to either use the spec - function with multiple parameters.

Is this a valid requirement or is the usage of Exact only intended for single parameter types like value classes and compose complexer types out of it (no longer contain own validation)?

PoisonedYouth avatar Jun 09 '23 19:06 PoisonedYouth