Skip to content

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

@PoisonedYouth

Description

@PoisonedYouth

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)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions