This episode covers the principles of building DSLs in Groovy, from the basic concepts to builder-style DSLs and declarative configuration. You will create DSL examples for deployment and testing that leverage closures, delegates, and methodMissing.

Groovy is most famous for its ability to build DSLs — domain-specific languages that make configuration read like human sentences. Gradle and Jenkins are the best examples: their users write configuration in a language that feels natural, not merely program code.
Episode 9 covers the principles of building DSLs, builder-style techniques with closures and delegates, declarative configuration, and DSL examples for deployment and testing.
A DSL is a language designed for one specific domain, not a general-purpose language. In the context of Groovy, a DSL typically leverages three language features:
The combination of these makes your code invoked in a style that declares what you want, not how to do it.
def jalankan = { closure ->
closure.delegate = this
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure()
}
def warna = "biru"
jalankan {
println "Warna favorit: ${warna}"
}closure.resolveStrategy = Closure.DELEGATE_FIRST makes the closure use the delegate's methods and properties first. This is what allows a configuration block to call methods from outside the closure's scope.
class ServerConfig {
String nama
List ports = []
void port(int nilai) {
ports << nilai
}
}
def server = new ServerConfig()
server.with {
nama = "api-prod"
port 8080
port 8443
}
println "${server.nama}: ${server.ports}"server.with { nama = "api-prod"; port 8080 } uses Groovy's built-in with method, which executes a closure in the context of an object. Each port call adds a value to the list, producing configuration that reads like a declaration.
class DeployDSL {
def lingkungan = []
def methodMissing(String name, args) {
println "Menyiapkan deployment ke ${name}"
lingkungan << name
this
}
def run() {
println "Deploy ke: ${lingkungan.join(", ")}"
}
}
def deploy = new DeployDSL()
deploy.staging().production().run()deploy.staging().production().run() calls staging and production methods that don't actually exist — both are captured by methodMissing, recorded, and return this for chaining.
class PipelineDSL {
List tahapan = []
List aktif = []
void stage(String nama, Closure body = null) {
aktif = []
if (body) {
body.delegate = this
body.resolveStrategy = Closure.DELEGATE_FIRST
body()
tahapan << "${nama}: ${aktif.join(", ")}"
} else {
tahapan << nama
}
}
void step(String nama) {
aktif << nama
}
void jalankan() {
tahapan.each { println "Jalankan: ${it}" }
}
}
def pipeline = new PipelineDSL()
pipeline.stage("Build") { step "compile"; step "package" }
pipeline.stage("Deploy")
pipeline.jalankan()pipeline.stage("Build") { step "compile" } shows a DSL accepting a closure containing sub-commands. body.delegate = this redirects step calls to the pipeline object, so stage details are recorded in a structured way. This is the pattern Gradle and Jenkins use in the real world.
class MathSpec {
def verifikasi = []
void describe(String nama, Closure body) {
body.delegate = this
body.resolveStrategy = Closure.DELEGATE_FIRST
body()
println "${nama}: ${verifikasi.size()} cek"
}
void cek(Boolean kondisi, String pesan) {
verifikasi << (kondisi ? "PASS" : "FAIL") + ": " + pesan
}
}
new MathSpec().describe("Penjumlahan") {
cek(2 + 2 == 4, "dua tambah dua sama dengan empat")
cek(5 * 3 == 15, "lima kali tiga sama dengan lima belas")
}describe("Penjumlahan") { cek(...) } defines a declarative test block. This describe and cek pattern is the precursor to Spock's syntax, which we'll learn in full in episode 11.
Episode 9 opened Groovy's most special capability: building DSLs. You learned the fundamental principles, closure and delegate techniques, methodMissing, and real DSL examples for deployment and testing.
The key takeaways:
resolveStrategy = Closure.DELEGATE_FIRST redirects the closure context.with method executes a closure in the context of an object.methodMissing captures calls to unrecognized methods.In episode 10 next, we'll discuss integration with Java and libraries — using Java libraries from Groovy seamlessly, importing Java packages, accessing enums, calling Java methods, and running Groovy inside Java applications and vice versa.