DEV Community

Cover image for Advanced Kotlin DSL: Building Smart Builders with Validation and Error Aggregation
Den B
Den B

Posted on

Advanced Kotlin DSL: Building Smart Builders with Validation and Error Aggregation

Kotlin DSLs are often used for configuration or UI building, but they rarely enforce correctness at compile or runtime. You can enhance DSLs with declarative validation logic, using internal builder patterns to collect multiple errors at once—an uncommon but highly powerful pattern.

✅ Use Case: Validating a DSL for Form Submission Logic

Instead of failing fast on the first error, we want to collect all configuration errors and then fail with full diagnostics.

⚙️ DSL with Validation Support

@DslMarker
annotation class FormDsl

@FormDsl
class FormBuilder {
    private val errors = mutableListOf<String>()
    private var name: String? = null
    private var email: String? = null

    fun name(value: String) {
        if (value.isBlank()) errors += "Name cannot be blank"
        name = value
    }

    fun email(value: String) {
        if (!value.contains("@")) errors += "Invalid email format"
        email = value
    }

    fun build(): Form {
        if (errors.isNotEmpty()) {
            throw IllegalArgumentException("Form validation failed:\n" + errors.joinToString("\n"))
        }
        return Form(name!!, email!!)
    }
}

data class Form(val name: String, val email: String)

fun form(block: FormBuilder.() -> Unit): Form =
    FormBuilder().apply(block).build()
Enter fullscreen mode Exit fullscreen mode

✅ Usage:

val form = form {
    name("")
    email("no-at-symbol")
}
// Throws with detailed error report:
// - Name cannot be blank
// - Invalid email format
Enter fullscreen mode Exit fullscreen mode

📌 Notes for Quick Reference
• ✅ Use errors: MutableList to accumulate configuration mistakes.
• ✅ Use @DslMarker to prevent scope collisions.
• ❌ Avoid throwing inside individual DSL methods—defer until .build().
• ✅ Ideal for CI/CD DSLs, Terraform-style Kotlin config, or complex form builders.

🚀 Pro Tip: Add warning() support next to error() to flag non-blocking suggestions during DSL execution, mimicking compiler diagnostics.

Top comments (0)