Examples

Pet clinic folder model

The complete reference model, split into a folder of seven .em.hcl files. The tool reads the folder as one model.

state_changestate_viewautomationtranslation

The rendered canvas

Generated with emhcl diagram examples/pet-clinic. Use the view switcher for Model, Compact, Storming, and Context Map. Click a slice for its scenarios.

How the files join

Every regular .em.hcl file directly in the folder whose name does not start with a dot is part of the model. Subfolders and other files are ignored. The tool sorts member files by name, so the number prefixes set the reading order. The prefixes are not part of the language.

A reference in one file can point to a block in any other file. For example, 10-register-pet.em.hcl uses events that 00-catalog.em.hcl declares. Each block stays whole in one file.

All chapter blocks are in one file. In this seven-file model, the chapter list sets the order of the workflows on the board. A workflow that is in no chapter gives diagnostic EM407: a warning by default, information in workshop, and an error in strict. See Folder models for all rules.

emhcl validate examples/pet-clinic emhcl diagram examples/pet-clinic -o pet-clinic.html emhcl serve examples/pet-clinic

The source files

Source folder: examples/pet-clinic/.

00-catalog.em.hcl

Catalog: the team, the partner system, the actor, and both bounded contexts with their events, aggregates, and field types. Raw file.

team "clinic_team" { title = "Clinic team" } system "partner_system" { title = "Partner system" external = true } actor "clinic_staff" { title = "Clinic staff" auth_required = true } bounded_context "clinic" { title = "Clinic" description = "Pet clinic operations and records." owner = team.clinic_team aggregate "pet" { title = "Pet" description = "A registered animal patient." } aggregate "owner" { title = "Owner" } field_type "pet_id" { cardinality = "Single" type = "UUID" id_attribute = true example = "07917485-4a6b-4e4e-8920-92ce6062a129" } field_type "pet_name" { mapping = "pet.name" schema = "PetName" type = "String" generated = false id_attribute = false optional = false pii = true technical_attribute = false example = "Mochi" } field_type "vaccinated" { type = "Boolean" example = true } field_type "weight" { type = "Double" example = 4.2 } field_type "fee" { type = "Decimal" example = 12.50 } field_type "microchip" { type = "Long" example = 1234567890 } field_type "birth_date" { type = "Date" example = "2020-01-12" } field_type "registered_at" { type = "DateTime" example = "2025-01-12T10:30:00Z" } field_type "pet_profile" { cardinality = "List" type = "Custom" example = [{ id = "07917485-4a6b-4e4e-8920-92ce6062a129" name = "Mochi" }] subfield "id" { type = "UUID" id_attribute = true } subfield "name" { type = "String" } } event "pet_registered" { title = "Pet Registered" description = "Records that a pet was registered." group_id = "registration" tags = ["event", "pet"] aggregate = aggregate.pet aggregate_dependencies = [aggregate.owner] service = null list_element = false prototype = { topic = "clinic.pet_registered" } sketched = false field "pet_id" { type = field_type.pet_id } field "pet_name" { type = field_type.pet_name } field "registered_at" { type = field_type.registered_at } } event "external_pet_imported" { field "pet_id" { type = field_type.pet_id } } event "owner_registered" { } } bounded_context "partner" { title = "Partner" owner = system.partner_system external = true event "pet_received" { title = "Pet Received" field "pet_id" { type = field_type.clinic.pet_id } } }

01-chapters.em.hcl

The one chapter. Its list sets the order of the workflows on the board. Raw file.

chapter "registration" { title = "Registration" workflows = [workflow.register_pet, workflow.pet_directory, workflow.notify_owner, workflow.import_partner_pet] }

10-register-pet.em.hcl

The register_pet state change, with its scenario. Raw file.

state_change "register_pet" { description = "Register a new pet for an owner." owner = bounded_context.clinic status = "created" screen "pet_screen" { title = "Pet screen" actor = actor.clinic_staff fields = [field_type.clinic.pet_name] to = [command.register_pet_command] field "pet_id" { } } screen_image "pet_form" { title = "Pet form" url = "https://example.test/pet-form.png" } command "register_pet_command" { title = "Register pet" description = "Records a pet." group_id = "registration" tags = ["write", "pet"] aggregate = aggregate.clinic.pet aggregate_dependencies = [aggregate.clinic.owner] api_endpoint = "POST /pets" service = null creates_aggregate = true triggers = ["submit"] list_element = false prototype = { route = "/pets", method = "POST" } sketched = false to = [event.clinic.pet_registered] field "pet_id" { type = field_type.clinic.pet_id } field "pet_name" { type = field_type.clinic.pet_name } field "request_id" { type = "UUID" technical_attribute = true } } table "pets" { title = "Pets" field "pet_id" { type = field_type.clinic.pet_id } } scenario "register_pet_specification" { title = "Register a pet" given { title = "Owner exists" tags = ["setup"] examples = [{ owner_id = 9 }] expect_empty_list = false event = event.clinic.owner_registered } when { title = "Register pet" command = command.register_pet_command field "pet_name" { type = field_type.clinic.pet_name } } then { title = "Pet registered" event = event.clinic.pet_registered } then { title = "Validation error" expect_empty_list = true error = "Pet registration is invalid" } comment { description = "The owner relationship is assumed to exist." } } }

11-pet-directory.em.hcl

The pet_directory state view. Raw file.

state_view "pet_directory" { title = "Pet directory" status = "done" readmodel "pet_summary" { title = "Pet summary" question = "Which pets are registered?" from = [event.clinic.pet_registered] to = [screen.pet_summary_screen] field "pet_profile" { type = field_type.clinic.pet_profile } } screen "pet_summary_screen" { title = "Pet summary screen" actor = actor.clinic_staff } }

12-notify-owner.em.hcl

The notify_owner automation. Raw file.

automation "notify_owner" { title = "Notify owner" status = "in_progress" readmodel "pets_needing_notification" { title = "Pets needing notification" question = "Which pets require an owner notification?" from = [event.clinic.pet_registered] to = [processor.pet_notification] } processor "pet_notification" { title = "Pet notification" to = [command.send_owner_notification] } command "send_owner_notification" { title = "Send owner notification" aggregate = aggregate.clinic.owner to = [event.clinic.external_pet_imported] } }

13-import-partner-pet.em.hcl

The import_partner_pet translation. Raw file.

translation "import_partner_pet" { title = "Import partner pet" description = "Translate the partner contract into the clinic language." processor "translate_pet" { title = "Translate partner pet" from = [event.partner.pet_received] to = [command.import_pet] } command "import_pet" { title = "Import pet" to = [event.clinic.external_pet_imported] } }

90-hotspots.em.hcl

The open hotspot on the notification processor. Raw file.

hotspot "notification_channel" { status = "open" question = "Which notification channel should be used?" on = processor.notify_owner.pet_notification }