---
title: "DSL em Kotlin: O que E e Como Funciona | Kotlin Brasil"
url: "https://kotlin.dev.br/glossario/dsl/"
markdown_url: "https://kotlin.dev.br/glossario/dsl.MD"
description: "Aprenda o que e DSL em Kotlin, como criar Domain-Specific Languages usando lambdas com receiver e type-safe builders."
date: "2026-02-10"
author: "Karina Melo"
---

# DSL em Kotlin: O que E e Como Funciona | Kotlin Brasil

Aprenda o que e DSL em Kotlin, como criar Domain-Specific Languages usando lambdas com receiver e type-safe builders.


## O que e DSL em Kotlin?

DSL significa **Domain-Specific Language** -- uma linguagem específica de dominio. Em Kotlin, você pode criar DSLs usando lambdas com receiver e [extension functions](/glossario/extension-function/), resultando em código que parece quase uma linguagem natural para determinada tarefa.

Se você já usou Gradle com Kotlin Script ou escreveu testes com Kotest, já viu DSLs em acao.

Pense numa DSL como um formulario bem desenhado: em vez de escrever texto livre, você preenche campos estruturados que guiam a resposta. A DSL faz isso com código -- cria uma estrutura para expressar intencoes de forma clara e restrita ao dominio.

O segredo por tras das DSLs em Kotlin esta na combinacao de [lambdas](/glossario/lambda/) com receiver, [higher-order functions](/glossario/higher-order-function/) e [extension functions](/glossario/extension-function/). Esses tres recursos juntos permitem criar APIs que parecem uma linguagem própria.

### Exemplo simples: construtor de HTML

```kotlin
class HTML {
    private val elementos = mutableListOf<String>()

    fun head(titulo: String) {
        elementos.add("<head><title>$titulo</title></head>")
    }

    fun body(conteudo: Body.() -> Unit) {
        val body = Body()
        body.conteudo()
        elementos.add("<body>${body.render()}</body>")
    }

    fun render() = "<html>${elementos.joinToString("")}</html>"
}

class Body {
    private val itens = mutableListOf<String>()

    fun p(texto: String) { itens.add("<p>$texto</p>") }
    fun h1(texto: String) { itens.add("<h1>$texto</h1>") }

    fun render() = itens.joinToString("")
}

fun html(bloco: HTML.() -> Unit): String {
    val pagina = HTML()
    pagina.bloco()
    return pagina.render()
}
```

E o uso fica assim:

```kotlin
fun main() {
    val pagina = html {
        head("Kotlin Brasil")
        body {
            h1("Bem-vindo!")
            p("Aprenda Kotlin de um jeito brasileiro.")
        }
    }
    println(pagina)
}
```

Repare como o código fica expressivo -- quase como escrever HTML de verdade.

### A magica: lambdas com receiver

O segredo das DSLs em Kotlin e a **lambda com receiver**. Quando você declara `HTML.() -> Unit`, esta dizendo que dentro do bloco, o `this` e uma instancia de `HTML`. Isso permite chamar os métodos diretamente, sem prefixo.

### Exemplo prático: configuração

```kotlin
class DatabaseConfig {
    var host = "localhost"
    var porta = 5432
    var nome = ""
}

fun database(config: DatabaseConfig.() -> Unit): DatabaseConfig {
    return DatabaseConfig().apply(config)
}

fun main() {
    val db = database {
        host = "db.kotlin.dev.br"
        porta = 5433
        nome = "kotlin_brasil"
    }
    println("${db.host}:${db.porta}/${db.nome}")
}
```

### Exemplo: DSL para definicao de rotas HTTP

Um caso muito comum em frameworks web e a definicao de rotas usando DSL. Veja como ficaria uma versão simplificada:

```kotlin
class Router {
    private val rotas = mutableListOf<String>()

    fun get(caminho: String, handler: () -> String) {
        rotas.add("GET $caminho -> ${handler()}")
    }

    fun post(caminho: String, handler: () -> String) {
        rotas.add("POST $caminho -> ${handler()}")
    }

    fun listar() = rotas.forEach(::println)
}

fun router(config: Router.() -> Unit): Router {
    return Router().apply(config)
}

fun main() {
    val api = router {
        get("/usuarios") { "Lista de usuarios" }
        get("/usuarios/{id}") { "Detalhes do usuario" }
        post("/usuarios") { "Criar usuario" }
    }
    api.listar()
}
```

### Exemplo: DSL para construcao de queries

```kotlin
class Query {
    private var tabela = ""
    private val condicoes = mutableListOf<String>()
    private var limite: Int? = null

    fun from(nome: String) { tabela = nome }
    fun where(condicao: String) { condicoes.add(condicao) }
    fun limit(n: Int) { limite = n }

    fun build(): String {
        val sql = StringBuilder("SELECT * FROM $tabela")
        if (condicoes.isNotEmpty()) {
            sql.append(" WHERE ${condicoes.joinToString(" AND ")}")
        }
        limite?.let { sql.append(" LIMIT $it") }
        return sql.toString()
    }
}

fun query(config: Query.() -> Unit): String {
    return Query().apply(config).build()
}

fun main() {
    val sql = query {
        from("pedidos")
        where("status = 'ativo'")
        where("valor > 100")
        limit(10)
    }
    println(sql)
    // SELECT * FROM pedidos WHERE status = 'ativo' AND valor > 100 LIMIT 10
}
```

### Controlando o escopo com @DslMarker

Quando você tem DSLs aninhadas, pode acontecer de o usuário acessar funções do escopo externo dentro do escopo interno por engano. A anotacao `@DslMarker` resolve isso:

```kotlin
@DslMarker
annotation class HtmlDsl

@HtmlDsl
class Table {
    fun tr(block: Row.() -> Unit) { /* ... */ }
}

@HtmlDsl
class Row {
    fun td(texto: String) { /* ... */ }
}
```

Com `@DslMarker`, dentro de um bloco `tr { }` você só tem acesso aos métodos de `Row`, não aos de `Table`. Isso evita erros e deixa a DSL mais segura.

### Casos de Uso no Mundo Real

- **Gradle Kotlin DSL**: toda a configuração de build do Gradle pode ser feita com Kotlin DSL, incluindo dependências, plugins e tarefas customizadas.
- **Jetpack Compose**: a UI declarativa do Android usa DSLs extensivamente. Cada [Composable](/glossario/composable/) e essencialmente uma DSL para construir interfaces.
- **Ktor**: o framework web da JetBrains usa DSL para definir rotas, configurar servidores e serializar dados.
- **Kotest**: framework de testes que usa DSL para descrever testes de forma expressiva, similar ao RSpec do Ruby.
- **Exposed**: framework de banco de dados da JetBrains que usa DSL type-safe para construir queries SQL.

### Boas Praticas

- Mantenha a DSL focada em um único dominio. Uma DSL que tenta fazer tudo vira uma GPL mal feita.
- Use `@DslMarker` para controlar o escopo e evitar que o usuário acesse funções de contextos externos indevidamente.
- Documente a DSL com exemplos claros, pois a sintaxe pode não ser obvia para quem não conhece a API.
- Prefira nomes que leiam como linguagem natural. `get("/rota")` e melhor do que `adicionarRotaGet("/rota")`.

### Erros Comuns

- **Nao usar @DslMarker**: sem essa anotacao, o usuário pode chamar funções do escopo externo dentro de blocos aninhados, causando comportamento inesperado.
- **DSL complexa demais**: se o usuário precisa ler a documentação inteira pra entender como usar, a DSL esta complicada demais. Simplifique.
- **Confundir DSL com builder pattern**: embora DSLs possam usar builders, nem todo builder e uma DSL. A DSL deve ter uma sintaxe que faça sentido no dominio.
- **Esquecer de validar entradas**: como a DSL permite ao usuário preencher campos livremente, e essencial validar os dados no momento da construcao do objeto final.

### Perguntas Frequentes

**Qual a diferenca entre DSL e API fluente?**
Uma API fluente usa encadeamento de métodos (method chaining), enquanto uma DSL usa lambdas com [receiver](/glossario/receiver/) para criar blocos de código que leem como uma linguagem própria. Em Kotlin, DSLs sao mais poderosas porque permitem aninhamento natural.

**Preciso saber [generics](/glossario/generics/) para criar DSLs?**
Nao necessariamente para DSLs simples, mas generics ajudam muito quando você quer criar DSLs reutilizaveis e type-safe. Muitas DSLs avancadas combinam generics com [inline functions](/glossario/inline/).

**DSLs afetam a performance?**
Minimamente. Como as lambdas com receiver sao compiladas para classes anonimas na JVM, há uma pequena alocacao de memória. Usando funções [inline](/glossario/inline/), esse custo e eliminado, pois o compilador insere o código diretamente no local da chamada.

**Posso criar DSLs que funcionem em Kotlin Multiplatform?**
Sim. Como DSLs sao construidas com recursos da própria linguagem Kotlin (lambdas, extension functions, receivers), elas funcionam em todas as plataformas suportadas pelo Kotlin, incluindo JVM, JS e Native.

DSLs em Kotlin sao poderosas para configuracoes, builders de UI, definicao de rotas em servidores web e muito mais. E uma das features que mais diferencia Kotlin de outras linguagens.
