Convert PlantUML to GORM (Go)

Generate production-ready GORM (Go) ORM code from your PlantUML diagrams in seconds.

🚀 View the Roadmap to see which frameworks are coming next.

gorm
Framework: gormLanguage: goTables: 2
Done.
plantuml

How the PlantUML to GORM (Go) conversion works

PlantUML's * markers map cleanly onto Go's type system: mandatory fields become plain value types, optional ones become pointers — the generator reads nullability straight from the diagram instead of a directive. Stereotypes handle keys (<<PK>>, <<FK>>, <<UNIQUE>>) and ' ::directives cover enums and defaults, producing structs AutoMigrate can apply as-is.

PlantUML entity diagrams are the standard in many enterprise and Java-adjacent teams, with first-class tooling in IDEs and Confluence. Entities declare mandatory fields with a leading *, and stereotypes like <<FK>> mark key columns.

Entities are `entity Name { … }` blocks — a leading `*` marks mandatory (NOT NULL) fields, `<<FK>>` marks foreign keys, and relationships use the same cardinality arrows as Mermaid (`User ||--o{ Post`). Quoted display names (`entity "User Account" as ua`) become table comments.

Paste your PlantUML source into the editor above and the engine parses it into an intermediate schema — tables, columns, relationships, indexes — then generates GORM (Go) code from that model. Anything the diagram syntax can't express (NOT NULL, defaults, enums, CHECK constraints, composite indexes) you add with comment directives like ' ::NN, ::DEFAULT(now()) on the line above a column. The PlantUML reference documents the full vocabulary.

Worked example: a library loan schema

A complete PlantUML diagram and the exact GORM (Go) output the generator produces for it — different from the starter diagram in the editor above, so you can see a second real conversion end to end:

@startuml
entity Member {
  * id : int <<PK>>
  * email : string <<UNIQUE>>
  name : string
  joined_at : timestamp
}
entity Book {
  * id : int <<PK>>
  * isbn : string <<UNIQUE>>
  title : string
  ' ::ENUM(available, loaned, lost)
  status : string
}
entity Loan {
  * id : int <<PK>>
  member_id : int <<FK>>
  book_id : int <<FK>>
  ' ::DEFAULT(now())
  loaned_at : timestamp
}
Member ||--o{ Loan : borrows
Book ||--o{ Loan : "loaned as"
@enduml
package models

import (
	"time"
)

type BookStatus string

const (
	BookStatusAvailable BookStatus = "available"
	BookStatusLoaned BookStatus = "loaned"
	BookStatusLost BookStatus = "lost"
)

type Member struct {
	ID uint `gorm:"column:id;primaryKey" json:"id"`
	Email string `gorm:"column:email;uniqueIndex;not null" json:"email"`
	Name *string `gorm:"column:name" json:"name"`
	JoinedAt *time.Time `gorm:"column:joined_at" json:"joined_at"`
	Loans []Loan `gorm:"foreignKey:MemberID" json:"loans,omitempty"`
}

type Book struct {
	ID uint `gorm:"column:id;primaryKey" json:"id"`
	Isbn string `gorm:"column:isbn;uniqueIndex;not null" json:"isbn"`
	Title *string `gorm:"column:title" json:"title"`
	Status *BookStatus `gorm:"column:status;type:enum(available,loaned,lost)" json:"status"`
	Loans []Loan `gorm:"foreignKey:BookID" json:"loans,omitempty"`
}

type Loan struct {
	ID uint `gorm:"column:id;primaryKey" json:"id"`
	MemberID *uint `gorm:"column:member_id" json:"member_id"`
	BookID *uint `gorm:"column:book_id" json:"book_id"`
	LoanedAt *time.Time `gorm:"column:loaned_at;default:now()" json:"loaned_at"`
	Member Member `json:"member,omitempty"`
	Book Book `json:"book,omitempty"`
}

Email and Isbn are plain string (their * made them mandatory), while Name and Title are *string — the pointer/value split mirrors the diagram's own markers, no ::NN needed anywhere. The ' ::ENUM directive became a BookStatus type with typed constants, and each relationship generated the navigation field on one side and the slice on the other.

What the generator emits for GORM (Go)

  • one Go struct per table with exported PascalCase fields
  • gorm struct tags: column names, primaryKey, uniqueness, not-null
  • association fields for one-to-many, many-to-one, one-to-one, and many-to-many relationships
  • ON DELETE constraint tags only when the diagram declares them — no surprise cascades
  • json tags alongside the gorm tags, ready for API serialization

Type mapping: PlantUML to GORM (Go)

Diagram column types are logical — the generator maps each one to the idiomatic GORM (Go) type. These are the exact mappings the engine uses:

Diagram typeGORM (Go) typeNotes
intuintGORM's conventional PK type
stringstring
textstring
decimalfloat64
booleanbool
datetime.Time
timestamptime.Timeimports time automatically
uuidstring
bigintuint64
blob[]byte

Keys, relationships, and enums

`int id PK` becomes `ID uint` tagged `gorm:"primaryKey"` — the GORM convention that AutoMigrate recognizes as an auto-increment key. Nullable columns become pointer types (*string, *time.Time).

Each relationship generates both sides: the FK scalar field on the child, a parent navigation field (`User User`), and a slice on the parent (`Posts []Post`). Cascade tags are only added when the diagram explicitly sets onDelete.

`::ENUM(active, inactive)` generates a named Go string type with typed constants for each value, and the struct field uses that type — so invalid states fail at compile time, not at runtime.

Frequently asked questions

How does PlantUML nullability become Go types?

A field with a leading * is mandatory, so it becomes a value type (string, time.Time); a field without it is nullable and becomes a pointer (*string, *time.Time). With Mermaid input the same split comes from the ::NN directive instead — PlantUML encodes it in the syntax itself.

What do the <<PK>> and <<FK>> stereotypes generate?

<<PK>> produces the ID field tagged gorm:"primaryKey"; <<FK>> produces the scalar foreign-key field plus the association fields on both structs. <<UNIQUE>> adds a uniqueIndex tag — all read directly from the diagram.

Do the generated structs work with GORM AutoMigrate?

Yes — fields carry primaryKey, not-null, unique, and column tags that AutoMigrate reads directly. The same diagram can also generate plain SQL DDL if you prefer explicit migrations.

How are nullable columns represented in Go?

As pointer types: a nullable string becomes *string, a nullable timestamp *time.Time. Non-nullable columns use value types.

Related conversions