Profiles

UML 2.x profiles are the OMG-standard extension mechanism: a profile defines stereotypes and tagged values that extend specific metaclasses. kUML treats profiles as first-class modules — five ship with v0.3.0, more are easy to add.

Built-in profiles

Profile Module Stereotypes (selected)

AUTOSAR

kuml-profile-autosar

SoftwareComponent, ComInterface, AutosarPort, Runnable, BehaviorSpec

JavaEE / Jakarta EE

kuml-profile-javaee

Entity, Repository, PersistenceContext, Service, Controller

Spring

kuml-profile-spring

RestController, SpringData, Scheduled

OpenAPI

kuml-profile-openapi

Resource, Schema, Operation, Parameter

SoaML

kuml-profile-soaml

Service, Participant, ServiceInterface, ServiceContract, ServicesArchitecture

All five are discovered through ServiceLoader — apply by name, no Maven coordinate gymnastics.

AUTOSAR example

AUTOSAR Classic Platform R22-11 modeled as type-safe Kotlin. Five stereotypes cover software components, communication interfaces, ports, runnables and behavior specs:

componentDiagram(name = "Brake Assist SWC") {
    applyProfile("AUTOSAR")

    component("BrakeAssist") {
        stereotype("SoftwareComponent", "kind" to "Application", "packageName" to "powertrain")

        port("wheelSpeedIn") { stereotype("AutosarPort", "direction" to "Required") }
        port("brakeCmdOut")  { stereotype("AutosarPort", "direction" to "Provided") }

        operation("evaluate") { stereotype("Runnable", "kind" to "Periodic", "periodMs" to 10L) }
        operation("onInit")   { stereotype("Runnable", "kind" to "OnInit") }
    }

    interface("WheelSpeed") {
        stereotype("ComInterface", "version" to "1.0", "isService" to false)
        operation("read")
    }
}

The «AutosarPort» stereotype is deliberately named that way (not Port) to avoid a clash with the UML metamodel’s own Port metaclass.

JavaEE example

classDiagram(name = "Domain") {
    applyProfile("JavaEE")

    classOf("Order") {
        stereotype("Entity", "tableName" to "orders")
        attribute("id", "UUID") { stereotype("Id") }
        attribute("notes", "String") { stereotype("Transient") }
    }
}

Authoring your own profile

Profiles are plain Kotlin objects implementing KumlProfile. Drop the file in any module, add a META-INF/services/dev.kuml.profile.KumlProfileProvider entry, and the CLI / Gradle plugin / IDE picks it up.

object MyProfile : KumlProfile {
    override val name: String = "MyCompany"

    override fun stereotypes(): List<KumlStereotype> = listOf(
        KumlStereotype(
            name = "Aggregate",
            extending = setOf(UmlMetaclass.Class),
            taggedValues = listOf(
                KumlTaggedValue(name = "boundedContext", type = "String"),
            ),
            constraints = listOf(
                KumlStereotypeConstraint(
                    name = "MustHaveIdAttribute",
                    body = "self.ownedAttribute->exists(a | a.appliedStereotype.name = 'Id')",
                ),
            ),
        ),
    )
}

class MyProfileProvider : KumlProfileProvider {
    override fun profile(): KumlProfile = MyProfile
}

META-INF/services/dev.kuml.profile.KumlProfileProvider:

com.example.MyProfileProvider

That’s the full registration. After applyProfile("MyCompany"), stereotype("Aggregate", …​) works in any class diagram.

Stereotype validation

When you applyProfile, kUML enforces:

  • The stereotype name exists in the profile.

  • The stereotype’s extending set contains the metaclass of the host element (so you can’t put Entity on an UmlOperation).

  • Tagged values are typed — you can’t pass a String where Int is required.

  • Any constraints attached to the stereotype validate at kuml validate time.

Mismatches are reported with the constraint name, the host element, and the OCL body — the same format as user-written constraints.

Profiles and code generation

The Java and SQL generators inspect applied stereotypes from the JavaEE profile and emit matching framework annotations / DDL:

Stereotype Java output SQL output

Entity

@jakarta.persistence.Entity(name = "…​")

CREATE TABLE name (…​)

Id

@jakarta.persistence.Id

PRIMARY KEY

Column

@jakarta.persistence.Column(name = "…​")

column_name TYPE

Transient

@jakarta.persistence.Transient

(column omitted)

The generators read stereotypes by name, not by class reference — they have no compile-time dependency on the profile modules. You can apply the same Java codegen to a model that uses a custom MyCompany-flavoured profile, as long as the stereotypes have the same names.

Multi-profile diagrams

applyProfile is repeatable — apply as many as you need:

classDiagram(name = "Service domain") {
    applyProfile("JavaEE")
    applyProfile("Spring")
    applyProfile("OpenAPI")

    classOf("OrderService") {
        operation("scheduleCleanup") { stereotype("Scheduled", "cron" to "0 0 * * *") }
        operation("placeOrder") { stereotype("Operation", "method" to "POST") }
    }
}

Stereotype names must be unique across applied profiles within a single diagram — the parser fails fast on collisions with a clear error.