Code Generation
kUML ships three code generators out of the box. They share a CodeGenRegistry
infrastructure, so authoring a new generator follows the same pattern as a theme or
profile — META-INF/services registration, done.
Built-in generators
| Generator | Output |
|---|---|
|
Kotlin data classes for each |
|
Java POJOs, records, or Lombok-annotated classes. Honours JavaEE / JPA stereotypes
for |
|
DDL for PostgreSQL (default), MySQL, H2, or SQLite. Topologically sorted
|
Generating Kotlin
kuml generate model.kuml.kts \
--plugin kotlin \
--package com.example.domain \
--output gen/
Output structure:
gen/
└── com/
└── example/
└── domain/
├── Order.kt
├── OrderItem.kt
└── OrderStatus.kt
The Kotlin generator produces idiomatic data classes with no framework dependencies.
Operations become method stubs marked TODO() — kUML generates signatures, not
implementations. That’s a deliberate choice: model the contract, write the body in Kotlin.
Generating Java
kuml generate model.kuml.kts \
--plugin java \
--package com.example.domain \
--options java-style=records \
--output gen/
java-style options:
-
pojo(default) — classic class with private fields and getters/setters -
records— Java 16+ record types -
lombok—@Data-annotated POJO (requires the Lombok dependency)
The Java generator inspects applied JavaEE stereotypes:
classOf("Order") {
stereotype("Entity", "tableName" to "orders")
attribute("id", "UUID") { stereotype("Id") }
attribute("notes", "String") { stereotype("Transient") }
}
produces:
@jakarta.persistence.Entity(name = "orders")
public class Order {
@jakarta.persistence.Id
private UUID id;
@jakarta.persistence.Transient
private String notes;
// getters/setters omitted
}
JavaEE annotations are emitted fully qualified — that side-steps import collisions and makes the output self-documenting. Lombok-mode is the exception (mainstream pattern, short forms expected).
Generating SQL DDL
kuml generate model.kuml.kts \
--plugin sql \
--options dialect=postgres,sql-drop=true \
--output gen/
dialect options: postgres (default), mysql, h2, sqlite.
sql-drop=true prepends DROP TABLE IF EXISTS … statements (handy for migrations).
The output is a single schema.sql with:
-
Optional
DROP TABLEblock -
CREATE TYPEfor enum types (Postgres only — other dialects useVARCHARwithCHECK) -
CREATE TABLEstatements, topologically sorted so referenced tables exist before referrers -
ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY …block at the end (avoids circular FK problems during deployment)
Multiplicity translates to constraints:
| UML multiplicity | SQL |
|---|---|
|
|
|
|
|
|
Stereotype-driven mapping:
-
Entity{tableName="users"}→CREATE TABLE users (…) -
Id→PRIMARY KEY -
Column{name="created_at"}→ renames the column
Generating from Gradle
The Gradle plugin’s kumlGenerate task takes the same options through the extension:
kuml {
generator.set("java")
generatePackage.set("com.example.domain")
generateOptions.put("java-style", "records")
}
kumlGenerate writes to ${buildDir}/kuml/generated/ by default. Configure
outputDir.dir("generated") to override.
Authoring a custom generator
Implement KumlCodeGenerator and provide a KumlCodeGeneratorProvider:
class TypeScriptCodeGenerator : KumlCodeGenerator {
override fun generate(
diagram: KumlDiagram,
outputDir: File,
options: Map<String, String>,
): List<File> {
// emit .ts files, return the list of written files
}
}
class TypeScriptCodeGeneratorProvider : KumlCodeGeneratorProvider {
override val name: String = "typescript"
override fun create(): KumlCodeGenerator = TypeScriptCodeGenerator()
}
Register via META-INF/services/dev.kuml.codegen.api.KumlCodeGeneratorProvider. CLI/Gradle
pick it up via --plugin typescript / generator.set("typescript").
The KumlCodeGenerator API is intentionally minimal — diagram + outputDir + options in,
list of written files out. Generators decide their own directory layout.