Learn · 02 of 5

The four patterns

Four causal shapes organize Event Modeling workflows.

PatternCore questionTypical flowHuman?External boundary?HCL workflow
State ChangeWhat does someone want to change?Screen/API → Command → EventOftenNo requirementstate_change
State ViewWhat does someone need to know?Event → Read Model → ScreenOftenNo requirementstate_view
AutomationWhat must the system do next by itself?Internal Event → Todo-list Read Model → Processor → Command → EventNoNo external Event inputautomation
TranslationHow does the system turn a fact from another context into the language of this model?External Event → Todo-list Read Model → Processor → Command → Internal EventNoYestranslation

Quick check: which pattern turns intent into fact, and which pattern turns facts into an answer?

Use one workflow per pattern-sized capability. Placing an order and showing its details are separate State Change and State View workflows, not one combined capability.

State Change

A user or another trigger wants something to happen. The system evaluates the Command. If the system accepts the Command, one or more Events record the resulting facts.

Screen / API -> Command -> Event actor "scheduler" { auth_required = true } bounded_context "appointments" { aggregate "appointment" { } event "appointment_added" { aggregate = aggregate.appointment } } state_change "schedule_appointment" { screen "schedule_appointment_ui" { actor = actor.scheduler to = [command.add_appointment] } command "add_appointment" { aggregate = aggregate.appointments.appointment to = [event.appointments.appointment_added] } }

The bounded context owns the Event. A bounded context is a part of the system with its own language. The workflow references the Event. A Command that an API triggers can use api_endpoint instead of a Screen.

State View

The facts already exist. A Read Model is an answer to one question. It folds or interprets those facts so that a user or an API can get the answer.

Event -> Read Model -> Screen actor "calendar_user" { auth_required = true } 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" { actor = actor.calendar_user } }

You write the flow from a catalog Event to a Read Model as readmodel.from. You write the flow from a Read Model to a Screen as readmodel.to.

Scenario: GIVEN Event(s) → THEN Read Model.

Automation

The system reacts to its own internal facts. It issues a new Command without a user screen. A Processor is the machine step that issues the Command.

Internal Event -> Read Model -> Processor -> Command -> Event 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] } }

An Automation consumes internal Events. External input belongs in a Translation.

Design the pending-work list with its Processor. Opening Events add items. Completion Events remove them. Document business failure and technical retry behavior.

List membership means work is pending. Do not replace it with a done flag. In this example, the forecast Event closes the appointment's pending item.

Translation

Another context or external system uses its own language. A Translation takes an external fact and turns it into a Command and an Event in the language of the receiving context.

External Event -> Todo-list Read Model -> Processor -> Command -> Internal Event system "weather_provider" { external = true } bounded_context "weather_provider" { external = true owner = system.weather_provider event "weather_forecast_changed" { } } bounded_context "weather" { event "updated_weather_prediction" { } } translation "translate_weather_change" { readmodel "changed_predictions" { question = "Which external weather predictions changed?" from = [event.weather_provider.weather_forecast_changed] to = [processor.translator] } processor "translator" { to = [command.translate_changed_weather] } command "translate_changed_weather" { to = [event.weather.updated_weather_prediction] } }

A Translation consumes an Event from an external bounded context. Legal HCL also permits direct Event input to a Processor. The method still requires a todo list.

A translation list has no completion edge. It relays external facts rather than tracking a worker's finished tasks.

Recover the internal business identity before recording the internal fact. Convert foreign units and data shapes, and keep external identifiers only for correlation or duplicate detection.

Specify missing correlation, invalid data, repeated delivery, and out-of-order arrival. Repeated delivery must not record the same business fact twice.

Add a worker after translation only when it makes a new business decision. Otherwise, project views directly from the translated Event.

See todo-list guidance and translation guidance for discovery questions and review steps.