hecks

Handler

Words available inside transition do ... end.

The tables on this page are generated from the language’s own aggregate-local syntax tables (lib/hecks/language/**/*.bluebook) by hecks language_run.project_reference — do not edit inside the markers. The prose between them is hand-written and survives regeneration.

```ruby boot Hecks::Adapters::Folder.new.load_bluebooks(File.join(InMemoryDomain::ROOT, “examples/banking/bluebook”))

Hecks.hecksagon(“Banking”) do attaches “Governance” Banking::Customer.persisted_by(“Memory”) Banking::Account.persisted_by(“Memory”) Banking::OnboardingCase.persisted_by(“Memory”) end Hecks.hecksagon(“Governance”) do Governance::RoleAssignment.persisted_by(“Memory”) Governance::RoleTransition.persisted_by(“Memory”) end


## dispatch

<!-- generated:begin word=dispatch -->
`dispatch command_name, command_name, with: do ... end` / `dispatch command_name, command_name, with:` — opens a `Dispatch` body

| argument | kind | required | fills |
|---|---|---|---|
| positional 1 | constant | true | command_name |
| positional 1 | text | true | command_name |
| `with:` | pairs | false | with_spec |
<!-- generated:end -->

Fires a command from inside a handler leg, mapping the saga instance's
own fields onto the command's arguments via `with:`. A bare command
constant, same as a policy's `trigger` — `Aggregate::Command` — resolved
same-domain by default, or `Domain::Aggregate::Command` to reach another
domain directly; there is no separate `across` here, the qualified
constant carries it.

`examples/banking`'s `Onboarding` saga carries one dispatching leg, and
`with:` is where the saga's own memory of the opening event becomes the
command's arguments — `customer: :customer` reads the field the
saga remembered, and `kind:`/`daily_limit:` are literals the saga
supplies itself:

```ruby skip
# examples/banking/bluebook/
transition "OnboardingCleared" => "cleared", from: "screening" do
  dispatch Account::Open, with: {
    customer: :customer, number: :account_number,
    kind: { name: "current" }, daily_limit: { cents: 0 }
  }
end

Nothing at the call site names an account. Clearing the case is the whole act, and the account exists afterwards:

runtime.dispatch("Banking::Customer.Register", with: { reference: { value: "hd-1" },
                                                       name: { given: "Annie", family: "Easley" },
                                                       email: { address: "annie@example.com" } })
kase = Banking::OnboardingCase.open!(customer: "hd-1", reference: { value: "hd-c1" },
                                    account_number: { value: "hd-a1" })
kase.clear!

Banking::Account.find("hd-a1")[:customer]  # => "hd-1"

The delivery is recorded, so a leg that fired and a leg that never ran are distinguishable after the fact:

runtime.registry.saga_log.last[:dispatch]   # => "Account.Open"
runtime.registry.saga_log.last[:delivered]  # => true