Learn · 01 of 5

The five elements

Five pieces appear across the four workflow patterns.

Event: a fact that already happened

An Event is a concrete fact in the domain. Its name is in the past tense: Appointment Added, Pet Added, Payment Received, Weather Forecast Changed.

In .em.hcl, a bounded context owns each Event. A bounded context is a part of the system with its own language. The Event is the canonical contract:

bounded_context "appointments" { event "appointment_added" { } }

The label is the identity. The label appointment_added gets the derived title Appointment Added. To use different wording, add title.

Rule of thumb: if the system can still reject the thing, it is probably not an Event yet.

Command: an intent to make something happen

A Command is a request or an intent. It can succeed or fail: Add Appointment, Add Pet, Cancel Subscription.

Commands live inside workflows. A workflow is one flow of elements. Each Command points to the Events that it can produce:

bounded_context "appointments" { aggregate "appointment" { } event "appointment_added" { aggregate = aggregate.appointment } } state_change "schedule_appointment" { screen "schedule_appointment_ui" { to = [command.add_appointment] } command "add_appointment" { aggregate = aggregate.appointments.appointment to = [event.appointments.appointment_added] } }

A Command needs an incoming flow, an api_endpoint, or external_trigger. If it has none of these, the validator reports EM404.

The method requires exactly one issuer for each Command. Identify the human decision or machine reaction, not just a validator reason.

A Command checks its documented preconditions against prior facts about its subject, not a Read Model. A refused intent records no Event. A tracked business failure can record a failure Event.

Read Model: an answer to a concrete question

A Read Model answers one concrete question.

No question, no Read Model.

The language requires question to enforce this rule:

bounded_context "appointments" { event "appointment_added" { } } state_view "view_calendar" { readmodel "calendar" { question = "Which appointments are on the calendar?" from = [event.appointments.appointment_added] to = [screen.calendar_ui] } screen "calendar_ui" { } }

"What data is needed?" is vague. "Which appointments do not yet have a weather forecast?" defines the projection. A projection is a view that the system builds from Events.

Screen: where an actor sees or starts behavior

A Screen is a UI, a rough wireframe, or another point where a human interacts with the system.

bounded_context "appointments" { event "appointment_added" { } } actor "calendar_user" { auth_required = true } state_view "view_calendar" { readmodel "calendar" { question = "Which appointments are on the calendar?" from = [event.appointments.appointment_added] to = [screen.calendar_ui] } screen "calendar_ui" { actor = actor.calendar_user } }

Actors are reusable catalog declarations. You reference them with traversals such as actor.calendar_user.

Model one committed decision per Screen state. If a page offers several actions, use separate Screen states, even when those actions are related.

Use a Processor when no human acts. Give human roles domain names and document what each role can and cannot do.

Automation (gear): a machine reaction

On the visual cheat sheet, the gear means that the computer reacts instead of a human.

Visual gear concept → processor block.
Automation pattern → automation workflow block.

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] } }

The automation block is a top-level workflow kind. Its machine reaction is a processor.

Every automation needs a todo-list Read Model under the method, including a simple relay. A worker list contains pending items, not completed records with a status flag. Its completion Events remove items.

Fields need a source

Trace each field across the connected flow: Screen input, Command, Event, Read Model, and displayed output. Keep names consistent or document a rename.

Document calculations and system-filled values with description. Use generated = true for system-filled values, not user input.

The method uses mapping vocabulary to explain lineage. It is not a validated HCL mini-language. Do not encode session:, latest:, or derived: expressions in a mapping attribute.

If a source is missing, inspect connected fields before adding data. Record a genuine unknown as a hotspot.

See the methodology on elements and fields for the full review.