Declaring an event
An event records that something happened, so that other code can react
to it later without the action that caused it waiting. You declare it as
a type whose base is Event, named in the past tense, one per file in
the domain's events/ folder.
domains/planning/events/todo_completed.jadpo:
use planning/entities { Todo }/// A todo was marked complete.type TodoCompleted = Event { todo_id: Todo.id completed_at: Instant}
TodoCompleted says a todo was completed, which todo and when. Event and
Instant are built in. Todo is your app's own entity, a kind of stored
record, and the use line brings it in from its file:
domains/planning/entities/todo.jadpo:
/// Something to do.entity Todo { id: Uuid done: Bool policy: { create: [Access.public] read: [Access.public, Access.system] update: [Access.public] }}
Access.public lets anyone in, which keeps this example short
(Policy), and jadpo migrate new initial makes the tables.
An action emits the event, and a
subscriber reacts to it, so the next two topics finish this
example. public type instead of type lets another domain subscribe to it
(Reacting in another domain).
What an event holds
An event is written as a failure is (Declaring a failure):
type X = Event { ... }, as type X = Failure { ... }. Its fields
follow the ordinary type rules.
Carry ids and the facts of the moment, not whole records. A subscriber that needs more reads the current state.
One type per event
Each event is its own type. There are no event families with variants,
such as TodoEvent.completed, so subscribing to an event and matching on
it never depend on a list that grows.
An event nobody uses
An event nothing subscribes to is reported like any declaration nothing uses.
The event record
An event's shape is recorded, and a change to it is shown as a diff, as
an API's shape is (The public API record).
This matters because events waiting in the outbox when a new build ships
are read with the new shape. So every event is recorded, not only
public ones.
Each domain that declares an event keeps one record beside its events,
domains/<domain>/events/events.jadpo_record (written by
jadpo records update; never edit it by hand). It is a view in the
record format (record events 1), whose header says it is generated
from "the events of domain <domain>". There is no command of its own:
jadpo records update writes it, and jadpo records lists it.
The record holds an event <Name> entry for each event, marked public
when another domain may subscribe to it. Under each entry are its fields
as its payload carries them, in the API record's wire form
(words: Text { max_length: 200 }). Then comes an object, enum,
input or output entry for each type a field names, written as the API
record writes them. Events, fields and types are sorted by born name.
Descriptions are left out, as in the API record.
The record is checked as the API record is
(What is committed, and why).
A missing record, one whose events changed (record.out_of_date, naming
each entry added, changed or removed), and the record of a domain left
with no events, are all put right by jadpo records update. A hand edit
Jadpo couldn't have written is record.edited. While an event file has
errors, the record isn't compared.
Events waiting in the outbox are read with the new shape, and subscribers
read what an event carries, so an event is both sent and read. A change
that breaks either needs a person's
acceptance, as the API
record's breaks do: an event removed (planning.TodoCompleted: ApiChange.event_removed), a field removed or retyped
(ApiChange.output_field_removed, .output_field_retyped), a required
field added (.input_field_required) or a rule tightened
(.rule_tightened), each named <domain>.<Event>.<field>.
Markdown for agents: /docs/declaring-an-event.md.Built from Jadpo main at aee2e8f.