PlantUML class diagrams: abstract, interface, association, dependency, qualifiers

puml.online

The class diagram is the most-used, most-misused UML diagram. Easy to read in a single example; harder to draw when domain semantics get complex. This walks every relationship type once, end to end.

Class member visibility

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@startuml
class User {
-id: Long
-name: String
#email: String
~passwordHash: String
+createdAt: LocalDate
+login()
+logout()
{static} +hashPassword()
{abstract} #validateEmail()
}

note right of User
- private
# protected
~ package / internal
+ public
{static} static
{abstract} abstract
end note
@enduml
Modifier Meaning
- private
# protected
~ package / internal
+ public
{static} static method
{abstract} abstract method
{method} regular method (usually omitted)

Class, abstract class, interface, enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@startuml
abstract class Animal {
+name: String
{abstract} +makeSound()
}

class Dog {
+breed: String
+makeSound()
}

class Cat {
+indoor: boolean
+makeSound()
}

interface Pet {
+play()
}

interface Named {
+getName(): String
}

enum Color {
BLACK
WHITE
TABBY
}

Animal <|-- Dog
Animal <|-- Cat
Animal ..|> Named
Pet ..|> Dog
Pet ..|> Cat
@enduml
  • abstract class renders italic
  • class regular
  • interface renders as <<interface>> stereotype with a hollow circle
  • enum for enumerations
  • <|-- inheritance (solid + open triangle)
  • ..|> realization (dashed + open triangle)

5 relationships between classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@startuml

class Car
class Engine
class Driver
class Wheel
class SteeringWheel

Car "1" *-- "4" Wheel : composition
Car "1" o-- "1" Engine : aggregation
Car "1" --> "1" SteeringWheel : association
Driver --> Car : dependency
Car ..> Driver : generalization (rare)

note bottom of Car
*-- composition — whole dies, parts die
o-- aggregation — whole dies, parts live on
--> association — long-lived reference
..> dependency — temporary usage
--|> generalization — subclass
..|> realization — implements interface
end note
@enduml
Relation Keyword Meaning Code form
Composition *-- whole/part, parts die with whole new inside
Aggregation o-- whole/bunch, parts can survive injected via constructor
Association --> long-term reference field
Dependency ..> temporary method param / local
Generalization --|> subclass extends
Realization ..|> interface impl implements

Modifiers on associations: multiplicity, role, visibility

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@startuml
class Company
class Employee

Company "1..*" o-- "*" Employee : employs

note right of Company
1..* is multiplicity
'employs' is the role name on this end
visibility like '-' '+' '#' goes by the role name
end note

Company --> "- boss +name" Employee : role + visibility
@enduml

Multiplicity is min..max:

  • 1 exactly one
  • 0..1 zero or one
  • * zero or more (no upper bound)
  • 1..* at least one

Role name sits next to the target. - boss means “Employee is the private boss role on Company”.

Nested classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@startuml
class Order {
-id: Long
-items: List<Item>
+checkout()
}

class OrderStatus {
+PENDING
+PAID
+SHIPPED
+DELIVERED
}

Order +-- OrderStatus : inner enum
@enduml

+-- says Order contains OrderStatus, renders as inner + outer nested box.

Package dependencies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@startuml
package "com.app" {
package "domain" {
class Order
class Customer
}
package "service" {
class OrderService
class CustomerService
}
package "controller" {
class OrderController
}
}

OrderService --> Order : references
OrderController --> OrderService : calls
CustomerService --> Customer : references
@enduml

package nested for module topology / dependency graph.

Abstract methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@startuml
abstract class Handler {
{abstract} +handle(req: Request): Response
+next(req: Request): Response
}

class AuthHandler {
+handle(req: Request)
}

class LoggingHandler {
+handle(req: Request)
}

Handler <|-- AuthHandler
Handler <|-- LoggingHandler

note bottom of Handler
{abstract} marks abstract methods
Subclasses must override
end note
@enduml

Composition vs aggregation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@startuml
class House {
-address: String
+rooms: List<Room>
}

class Room {
-area: int
}

class Garage

House "1" *-- "*" Room : composition
House "0..1" o-- "0..1" Garage : aggregation

note right of Garage
Composition: tear down the house, the rooms vanish.
Aggregation: tear down the house, the garage is salvageable.
end note
@enduml

Rule: “if the whole dies, can the part live on?” — yes → o--, no → *--.

Association class (join entity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@startuml

class Student
class Course
class Enrollment {
-grade: Float
-enrolledAt: Date
}

Student "1" --- "*" Enrollment
Course "1" --- "*" Enrollment
(Student, Course) .. Enrollment

note right of Enrollment
Enrollment is the association class of Student and Course.
It owns its own properties (grade, enrolledAt).
end note
@enduml

Use --- for the two associations and .. to the middle class. Common in many-to-many join tables.

Real example: e-commerce domain model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@startuml
skinparam classAttributeIconSize 0

abstract class AggregateRoot {
+id: Long
{abstract} +validate()
}

class Order {
-status: OrderStatus
-items: List<OrderItem>
-customerId: Long
+place()
+cancel()
+ship()
+complete()
+totalAmount(): Money
}

class OrderItem {
-productId: Long
-quantity: int
-unitPrice: Money
}

class Money {
-amount: BigDecimal
-currency: String
}

class Customer {
-name: String
-email: String
}

class Product {
-sku: String
-name: String
-price: Money
}

enum OrderStatus {
PENDING
PAID
SHIPPED
DELIVERED
CANCELLED
REFUNDED
}

interface Auditable {
+createdAt: Instant
+updatedAt: Instant
}

AggregateRoot <|-- Order
Auditable ..|> Order
Auditable ..|> Customer

Order "1" *-- "1..*" OrderItem
OrderItem "1" --> "1" Product
Order "1" --> "1" Customer
Order "1" --> "1" OrderStatus
OrderItem "1" --> "1" Money
Product "1" --> "1" Money

note bottom of Order
Order model notes:
- Order is the aggregate root
- Composes 1..* OrderItems
- References Customer / Product (no composition)
- Status enum expresses lifecycle
end note
@enduml

Review checklist

  • Every class has visibility markers on fields / methods (- / # / +)?
  • Relation arrows point the right way? (A holds B → A --> B; reverse if opposite)
  • Distinguish generalization / realization / association / aggregation / composition / dependency?
  • Don’t draw all 5 relations for every pair; only the semantically meaningful ones
  • Interfaces use <<interface>> or the dedicated interface keyword
  • Abstract methods marked {abstract} or italic
  • Abstract classes use abstract class
  • Enums use enum
  • Abstract class name starts with Abstract (per project convention)

Common anti-patterns

Too many relationships

1
2
3
4
5
6
7
class A
class B
class C
A --> B
B --> C
C --> A
@enduml

A → B → C → A cycle. Mixing runtime and compile-time dependencies in one picture confuses reviewers.

No visibility

1
2
3
4
5
6
class User {
id
name
email
login()
}

Without visibility the reviewer is left guessing. Always mark.

Mutual references

1
2
3
4
5
class Foo
class Bar

Foo --> Bar : holds
Bar --> Foo : holds

Mutual references usually mean the modules should split, or the two classes should merge.

TL;DR

Class diagrams have 5 relationships + 4 visibilities + 5 structural types. Get the arrow direction, visibility, and semantics (aggregation vs composition vs association) right, and the diagram becomes professional.

  • Title: PlantUML class diagrams: abstract, interface, association, dependency, qualifiers
  • Author: puml.online
  • Created at : 2026-07-29 15:25:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-class-advanced-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.