Learn · 04 of 5

Scenarios: make the slice testable

A scenario sits beside its workflow. It describes one business rule with typed steps.

WorkflowGivenWhenThenCardinality
State ChangeEventCommandEvent or Errorzero or more given, one when, one or more then
State ViewEventnoneRead Model or Errorone or more given, zero when, one or more then
Automation / TranslationEvent or Read ModelProcessor or CommandEvent or Errorone when, one or more then

Each step has one typed target. An error target contains a literal string.

The v0.4.0 specification defines these HCL rules. Canvas examples in the methodology are conceptual, not alternative grammar.

State Change scenario

Given Event(s) When Command Then Event or Error bounded_context "owner_management" { event "owner_registered" { } } bounded_context "pet_management" { event "pet_added" { } } state_change "add_pet" { screen "add_pet_ui" { to = [command.add_pet_command] } command "add_pet_command" { to = [event.pet_management.pet_added] } scenario "add_pet_success" { given { event = event.owner_management.owner_registered } when { command = command.add_pet_command } then { event = event.pet_management.pet_added } } }

An error path uses a different then target. An optional comment records the business rule:

bounded_context "pet_management" { event "pet_added" { } } state_change "add_pet" { screen "add_pet_ui" { to = [command.add_pet_command] } command "add_pet_command" { to = [event.pet_management.pet_added] } scenario "add_pet_validation_error" { when { command = command.add_pet_command } then { error = "Pet name is required" } comment { description = "A pet must have a name before it can be registered." } } }

State View scenario

Given Event(s) Then Read Model or Error

For example: given Student Registered, then Students to Welcome.

bounded_context "students" { event "student_registered" { } } state_view "welcome_students" { readmodel "students_to_welcome" { question = "Which registered students still need a welcome?" from = [event.students.student_registered] } scenario "registered_student_needs_welcome" { given { event = event.students.student_registered } then { readmodel = readmodel.students_to_welcome } } }

A State View has one or more Event given steps, no when, and one or more Read Model or Error then steps.

If you work from an older draft, read the migration guide for the removed Query form.

Automation / Translation scenarios

The given step targets an Event or Read Model. The when step targets a Processor or Command. The then step targets an Event or Error.

bounded_context "appointments" { event "appointment_added" { } } bounded_context "weather" { event "weather_predicted_for_appointment" { } } automation "add_weather_forecast" { readmodel "appointments_without_weather_forecast" { question = "Which appointments still need a weather forecast?" description = "Appointment Added adds pending work. Weather Predicted for Appointment removes the matching item." from = [event.appointments.appointment_added, event.weather.weather_predicted_for_appointment] to = [processor.weather_processor] } processor "weather_processor" { to = [command.add_weather_forecast] } command "add_weather_forecast" { to = [event.weather.weather_predicted_for_appointment] } scenario "forecast_is_added" { given { event = event.appointments.appointment_added } when { processor = processor.weather_processor } then { event = event.weather.weather_predicted_for_appointment } } }

Cover behavior, not just syntax

For each Command, review success, invalid input, invalid state, duplicate actions, alternative outcomes, external failure, and compensation. Write each applicable case.

Use the actual business thresholds and concrete field values. Explain why a case cannot occur instead of inventing a rule.

Review Read Models separately, including todo lists. Cover population, updates, and removal from their source Events.

The methodology also uses storylines for lifecycles. HCL has no storyline block. Use several legal scenarios and comment blocks to explain related transitions.

A canvas automation can describe an empty When and a dispatched Command in Then. Native HCL requires one Processor or Command when, then an Event or Error.

Do not invent query or expect_empty_list attributes. A rejected Command uses an Error target, not an empty-list assertion. Record a tracked business failure as an Event.

See methodology scenario coverage for discovery questions. Keep forms that HCL cannot express in explanatory prose, not invented syntax.

Faded exercise: complete a State View

Fill in each ???, then open the answer.

state_view "show_pet_details" { readmodel "pet_details" { question = ??? from = [???] to = [???] } screen "pet_details_screen" { actor = ??? } scenario "pet_details_loaded" { given { event = ??? } then { readmodel = ??? } } }
Answer bounded_context "pet_management" { event "pet_added" { } } actor "clinic_staff" { auth_required = true } state_view "show_pet_details" { readmodel "pet_details" { question = "What are the current details of this pet?" from = [event.pet_management.pet_added] to = [screen.pet_details_screen] } screen "pet_details_screen" { actor = actor.clinic_staff } scenario "pet_details_loaded" { given { event = event.pet_management.pet_added } then { readmodel = readmodel.pet_details } } }