MicroUML offers a small Pharo embedded syntax to describe class diagrams.
Often we would like to describe simple class diagram in emails, discord or other textual channels. But we do not want to have load a specific parser infrastructure to be able to manipulate it.
MicroUML has been designed during ESUG 2025 at Gdansk with the following constraints
- only use the Pharo syntax
- be compact
- covers most of the UML class diagram
- supports simple relation
- produces a minimal metamodel objects
To build a MicroUML diagram, we can use a special class called MicroUMLAstBuilder or its alias MU. This class understands a message === which can be used to define classes. Take a look at the following example. We will explain all elements in the following sections.
uml := MicroUMLAstBuilder
===
#AbstractSeries % #abstract
--@ #TSearch
--@ #TArtifact
- #name @ String
- #numEpisodes @ Integer % #abstract % #private
|> #findByName ~#(String) @ #AbstractSeries
===
#NovelSeries
--|> #AbstractSeries
- #author @ String
- #Publisher @ String
> #read~{}
===
#ComicSeries
--|> #AbstractSeries
- #toonAuthor @ String
- #storyAuthor @ String
> #print~{}
===
#AnimeSeries
--|> #AbstractSeries
- #director @ String
- #animators @ String % #private
- #voiceActors @ String % #private
> #play~{}
<>-- #ComicSeries @ ('original' -> 'comicalize') %< '1' %> '0..*'
<>-- #NovelSeries @ ('main' ->'side stories') %< '1..*' %> '*'.Each === followed by a symbol #ClassName defines a class. The result is an object of class MicroUMLAstBuilder. We can use a diagram message to build an actual AST of our UML diagram:
uml diagramWe can also visualize this diagram directly using the extent: message. It requires a point as an argument, specifying the width @ height of the image.
uml extent: 800@400
Class definition starts with # and conceptually produces a UmlClassBox. Classes can be defined by sending === to MicroUMLAstBuilder or MU:
MU
===
#AbstractSeries They can also be defined by sending any of the association messages to a Symbol. In the example below, we define a class AbstractSeries by specifying that it is a subclass of Manga:
#AbstractSeries
--|> #MangaYou can read more about different associations in th following sections.
MicroUML supports two types of members for class diagrams: attributes and methods. Attributes are defined with - message with a symbol defining the attribute name. For example:
#Class --|> #Subclass
- #var1
- #var2Methods can be defined in a similar way using the > message:
#Class --|> #Subclass
> #method1
> #method2We can add arguments to a method with a ~ message followed by the collection of argument types:
#OrderedCollection --|> #Collection
> #do: ~ { #Block }
> #copyFrom:to: ~ { #Integer . #Integer }The type of attributes and the return type of methods can be specified with a @ message:
#Class --|> #Superclass
- #var1 @ #Integer
- #var2 @ #String
> #method1 @ #Float
> #method2 ~ { #Integer } @ #IntegerBy default, attributes are protected (accessible from the class that defines it and from the superclasses) and methods are public. We can use the % message to change the access modifiers:
#Class --|> #Superclass
- #var1 "protected by default"
- #var2 % #public
- #var3 % #private
- #var4 % #protected
> #method1 "public by default"
> #method2 % #public
> #method3 % #private
> #method4 % #protectedAccess modifiers can be combined with types and argument lists in any order:
#Class --|> #Superclass
- #var1 @ #Integer % #public
- #var2 % #public @ #Integer
> #method1 ~ { #String } % #private @ #StringTo define class attributes or class methods, add | before the definition. In other words, class attributes are defined using the |- message and class methods using the |> message:
#Class --|> #Subclass
|- #var1
|- #var2 @ #String % #public
|> #method1
|> #method2 ~ { #Integer } @ #String"Inheritance"
#Circle
--|> #Shape.
"Plain association with one role (left only)"
#Employee
-- #Company
@ #employer. "left role = employer"
"Plain association with both roles + multiplicities"
#Team
-- #Player
@ (#team -> #members)
%< '1';
%> '0..*'.
"Directed association (right) with right-only role"
#Order
=> #Customer
@ (#none -> #orders) "explicit right role"
%< '0..*'
%> '1'.
"Aggregation (undirected) with qualifier on the right end"
#Library
<>-- #Book
@ (#library -> #holdings)
%< '1'
%> '0..*'.
"Composition (right) with association class"
#Car
*=> #Wheel
@ (#car -> #wheels)
%< '1'
%> '4'
@= #Mounting.
"Dependency"
#Client
--> #Service
@ 'uses at runtime'.We use === to link multiple class definitions.
MU
===
#AbstractSeries
- #name @ #String
- #numEpisodes @ #Integer
===
#NovelSeries
--|> #AbstractSeries
- #author @ #String
- #publisher @ #String
> #readWe decided to avoid to manipulate classes as the receiver in the class definition (Object << #Point and not #Object << #Point)
This is why we extensively use Symbols. This gives regularity and writers do not have to know if the classes they refer exist or not.
Watch out we want to integrate it into Pharo so the repository will probably change.
Metacello new
baseline: 'MicroUML';
repository: 'github://olekscode/MicroUML:main';
load. spec
baseline: 'MicroUML'
with: [ spec repository: 'github://olekscode/MicroUML:main' ].S. Ducasse, T. Oda, O. Zaitsev

