Real World Example

Notification Rules DSL

A marketing team at an e-commerce company wants to define push-notification rules without touching app logic. Engineers give them a small Kotlin DSL; the rules read like a configuration file but are type-checked.

Example

@DslMarker annotation class RuleDsl

data class User(val name: String, val cartValue: Int, val daysInactive: Int, val city: String)

@RuleDsl
class Rule(val name: String) {
    val conditions = mutableListOf<(User) -> Boolean>()
    var message: (User) -> String = { "" }
    fun whenever(cond: (User) -> Boolean) { conditions += cond }
    fun send(msg: (User) -> String) { message = msg }
}

@RuleDsl
class Campaign {
    val rules = mutableListOf<Rule>()
    fun rule(name: String, init: Rule.() -> Unit) { rules += Rule(name).apply(init) }

    fun run(users: List<User>) = users.flatMap { u ->
        rules.filter { r -> r.conditions.all { it(u) } }.map { r -> "[${r.name}] to ${u.name}: ${r.message(u)}" }
    }
}

fun campaign(init: Campaign.() -> Unit) = Campaign().apply(init)

fun main() {
    val diwali = campaign {
        rule("abandoned-cart") {
            whenever { it.cartValue > 1000 }
            whenever { it.daysInactive >= 2 }
            send { "Your cart worth Rs ${it.cartValue} is waiting. Extra 10% off today!" }
        }
        rule("win-back") {
            whenever { it.daysInactive > 30 }
            send { "We miss you, ${it.name}! Here is Rs 150 off." }
        }
    }
    val users = listOf(
        User("Meera", 2400, 3, "Pune"),
        User("Kabir", 300, 45, "Delhi"),
        User("Tanvi", 5000, 0, "Chennai"),
    )
    diwali.run(users).forEach(::println)
}

Output

[abandoned-cart] to Meera: Your cart worth Rs 2400 is waiting. Extra 10% off today!
[win-back] to Kabir: We miss you, Kabir! Here is Rs 150 off.