Install VSCode Plugin and PUML Definitions
VSCode Plugin
Install the following plugin in VSCode
The Visualized Result
Now we make use of the plugin we just installed in vscode to preview the puml
file:
We get the live-preview visualized from the PlantUML definitions:
Custom Definition of Objects in PlantUML and Examples in DDD
lib_eventstorming.puml
, a Modularized File
lib_eventstorming.puml
, a Modularized FileIn this file we have defined the following syntax:
The ones we use most of the time:
Command
Aggrgate
Policy
Handler
The remaining comes in handy:
EsEntity
Arrow
FacadeCommmand
Result
Event
DomainEvent
IntegrationEvent
Query
ReadeModel
UserInterface
Service
Saga
Process
Timer
Person
System
Comment
!global $ICON_FORMAT="png" !global $TEXT_WIDTH_MAX=200 !global $MSG_WIDTH_MAX=150 !global $FONT_SIZE_XS=10 !global $FONT_SIZE_SM=12 !global $FONT_SIZE_MD=16 !global $FONT_SIZE_LG=20 !global $FONT_COLOR="#212121" !global $FONT_COLOR_LIGHT="#757575" !procedure EsEntity($shape, $stereotype, $id, $label="") !if ($label != "") $shape "$label" as $id <<$stereotype>> !else $shape $id <<$stereotype>> !endif !endprocedure show stereotype skinparam defaultTextAlignment center skinparam wrapWidth 400 skinparam maxMessageSize 150 skinparam Arrow { Color $FONT_COLOR FontColor $FONT_COLOR FontSize $FONT_SIZE_SM } ' definition of the Item eventstorming/Element/FacadeCommand skinparam file<<FacadeCommand>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #779fae } !procedure FacadeCommand($id, $label="") EsEntity('file', 'FacadeCommand', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Command skinparam file<<Command>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #aec6cf } !procedure Command($id, $label="") EsEntity('file', 'Command', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Result skinparam file<<Result>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #cfcfc4 } !procedure Result($id, $label="") EsEntity('file', 'Result', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Event skinparam file<<Event>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #ffb853 } !procedure Event($id, $label="") EsEntity('file', 'Event', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/DomainEvent skinparam file<<DomainEvent>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #ffcb81 } !procedure DomainEvent($id, $label="") EsEntity('file', 'DomainEvent', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/IntegrationEvent skinparam file<<IntegrationEvent>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #ffdeaf } !procedure IntegrationEvent($id, $label="") EsEntity('file', 'IntegrationEvent', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Query skinparam file<<Query>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #62d862 } !procedure Query($id, $label="") EsEntity('file', 'Query', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/ReadModel aram file<<ReadModel>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #77dd77 } !procedure ReadModel($id, $label="") EsEntity('file', 'ReadModel', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/UserInterface skinparam file<<UserInterface>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #a2e8a2 } !procedure UserInterface($id, $label="") EsEntity('file', 'UserInterface', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Aggregate skinparam file<<Aggregate>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #fdfd9d } !procedure Aggregate($id, $label="") EsEntity('file', 'Aggregate', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Service skinparam file<<Service>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #fcfc78 } !procedure Service($id, $label="") EsEntity('file', 'Service', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Policy skinparam file<<Policy>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #b6a2db } !procedure Policy($id, $label="") EsEntity('file', 'Policy', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Saga skinparam file<<Saga>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #c9bbe5 } !procedure Saga($id, $label="") EsEntity('file', 'Saga', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Process skinparam file<<Process>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #ddd4ee } !procedure Process($id, $label="") EsEntity('file', 'Process', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Timer skinparam file<<Timer>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #cfcfc4 } !procedure Timer($id, $label="") EsEntity('file', 'Timer', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Person skinparam actor<<Person>> { StereotypeFontSize 0 shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #ffd1dc } !procedure Person($id, $label="") EsEntity('actor', 'Person', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/System skinparam file<<System>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #ffd1dc } !procedure System($id, $label="") EsEntity('file', 'System', $id, $label) !endprocedure ' definition of the Item eventstorming/Element/Comment skinparam file<<Comment>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor transparent } !procedure Comment($id, $label="") EsEntity('file', 'Comment', $id, $label) !endprocedure skinparam rectangle<<Handler>> { StereotypeFontSize $FONT_SIZE_SM shadowing false FontColor $FONT_COLOR BorderColor $FONT_COLOR BackgroundColor #e8f4fd } !procedure Handler($id, $label="") EsEntity('file', 'Handler', $id, $label) !endprocedure
timetable.puml
The Diagram Result
The Code Implementation
Let's import the definition (the highlighted line) and draw the diagram as follows:
@startuml !include lib_eventstorming.puml actor Teacher as Teacher Aggregate("Student") together { Command("CreateCourseCommand") Command("UpdateCourseCommand") Command("DeleteStudentCommand") } Command("CreateStudentPackageCommand") together{ Command("CreateStudentCommand") Command("UpdateStudentCommand") Command("CreateCustomHolidayCommand") Command("AssignDayAsCustomHolidyCommand") } Handler("CreateCourseHandler") Event("CourseCreatedEvent") CreateCourseCommand --> CreateCourseHandler : "processed by" CreateCourseHandler --> CourseCreatedEvent Handler("UpdateCourseHandler") Event("CourseUpdatedEvent") UpdateCourseCommand --> UpdateCourseHandler : "processed by" UpdateCourseHandler --> CourseUpdatedEvent Handler("DeleteStudentHandler") Event("StudentDeletedEvent") DeleteStudentCommand --> DeleteStudentHandler : "processed by" DeleteStudentHandler --> StudentDeletedEvent Handler("UpdateStudentHandler") Event("StudentUpdatedEvent") UpdateStudentCommand --> UpdateStudentHandler : "processed by" UpdateStudentHandler --> StudentUpdatedEvent CreateCourseCommand-[hidden]down-CreateStudentCommand Handler("CreateCustomHolidayHandler") Event("CustomHolidayCreatedEvent") CreateCustomHolidayCommand --> CreateCustomHolidayHandler : "processed by" CreateCustomHolidayHandler --> CustomHolidayCreatedEvent Handler("CreateStudentHandler") Event("StudentCreatedEvent") CreateStudentCommand --> CreateStudentHandler : "processed by" CreateStudentHandler --> StudentCreatedEvent Event("DayAssignedAsCustomHolidayEvent") Handler("AssignDayAsCustomHolidyHandler") AssignDayAsCustomHolidyCommand --> AssignDayAsCustomHolidyHandler : "processed by" AssignDayAsCustomHolidyHandler --> DayAssignedAsCustomHolidayEvent together{ Command("CreateClassesCommand")[ CreateClassesCommand --validations-- **1.** The new total minutes of all classes cannot exceed the limit of the student package **2.** Cannot overlap with existing classes that the student possesses ] Aggregate("StudentPackage") Command("UpdateClassCommand") Command("RemoveClassCommand") Command("UpdateStudentPackageCommand") } CreateClassesCommand-[hidden]down-StudentPackage Policy("ClassOnHolidayMustExtendPolicy")[ Class On Holiday Must be Extended Policy -- **1.** A class on a custom holiday must be extended to another day of the same time **2.** That day will be the same as the class being extended ] ' Position ExtendClassCommand directly below the policy Command("ExtendClassCommand") Policy("ExtendClassPolicy")[ Extend Class due to Status Change Policy -- **1.** When class status was changed to LEGIT_ABSENSE, an extended class will be created **2.** When a class was changed from LEGIT_ABSENSE back to anything else, the corresponding extended class must be removed ] DayAssignedAsCustomHolidayEvent --> ClassOnHolidayMustExtendPolicy ClassOnHolidayMustExtendPolicy --> ExtendClassCommand ' Use hidden connection to force vertical alignment ClassOnHolidayMustExtendPolicy -[hidden]down- ExtendClassCommand together{ Event("ClassExtendedEvent") Event("ClassUpdatedEvent") Event("ClassRemovedEvent") Event("StudentPackageUpatedEvent") } Student --> StudentPackage Teacher --> CreateCourseCommand Teacher --> UpdateCourseCommand Teacher --> CreateStudentCommand Teacher --> UpdateStudentCommand Teacher --> DeleteStudentCommand Teacher --> CreateStudentPackageCommand Teacher --> UpdateStudentPackageCommand Teacher --> CreateClassesCommand Teacher --> UpdateClassCommand Teacher --> RemoveClassCommand Teacher --> ExtendClassCommand Teacher --> CreateCustomHolidayCommand Teacher --> AssignDayAsCustomHolidyCommand UpdateStudentPackageCommand --> StudentPackage CreateClassesCommand --> StudentPackage UpdateClassCommand --> StudentPackage RemoveClassCommand --> StudentPackage CreateStudentPackageCommand --> Student UpdateStudentPackageCommand-[hidden]down-StudentPackage StudentPackage --> ClassExtendedEvent StudentPackage --> ClassUpdatedEvent StudentPackage --> ClassRemovedEvent StudentPackage --> StudentPackageUpatedEvent ClassUpdatedEvent --> ExtendClassPolicy ExtendClassPolicy --> ExtendClassCommand ExtendClassPolicy --> RemoveClassCommand ExtendClassCommand --> StudentPackage actor Scheduler as Scheduler Command("CreateDeadlineComingNotificationCommand")[ CreateDeadlineComingNotificationCommand --rules-- **1.** When package official_endDate - today = 1month, a notification should be recorded in a table **2.** notification should have an identifier so that repeated notification will not be made again, e.g., {package_id}:deadline_coming:{officiend_endDdate} ] Handler("CreateDeadlineComingNotificationHandler") Event("DeadlineComingNotificationCreatedEvent") CreateDeadlineComingNotificationCommand --> CreateDeadlineComingNotificationHandler CreateDeadlineComingNotificationHandler --> DeadlineComingNotificationCreatedEvent Scheduler --> CreateDeadlineComingNotificationCommand @enduml
Some Useful Reposition Trick
Group Together
together { <element1> <element2> }
Align Vertically
Note that it is not strictly vertically aligned, the horizontal position is still calculated by the native algorithm
<element1>-[hidden]down-<element2>
Generate PDF File
I have built a docker image to produce a pdf file inside the container:
docker run --rm -v $(pwd):/data machingclee/plantuml-pdf -tpdf timetable.puml
GUI Application
Pladitor
When our diagram becomes very huge, it would be better to reach our code via clicking the diagram. Luckily there is already a GUI-application to achieve this called Pladitor:
it costs HKD 88 but now we can click our "card" to reach our code easily:
!include failed, now we use !includeurl
Unforturnately the !incldue macro is not available in pladitor, however we can upload the puml
definition to github and import it as follows:
@startuml !includeurl https://raw.githubusercontent.com/machingclee/2025-08-23-plantUML-config/refs/heads/main/lib_eventstorming.puml