Skip to content

Commit 6739a91

Browse files
authored
Merge pull request #27 from IntentFile/docs/outbound-departures
docs: outbound - departures on a queue or a topic
2 parents a450854 + 03c64c3 commit 6739a91

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

docs/reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ The quick lookup surface: one line and a minimal snippet per construct. For rule
5555
| [`schedules`](/spec/glue#schedules) | cron: notify or generate records per matching row |
5656
| [`integrations`](/spec/glue#integrations-outbound-http) | outbound HTTP on a data change |
5757
| [`inbound`](/spec/glue#inbound-arrivals-from-outside) | records arriving from outside: a webhook, a queue/topic message, a dropped file |
58+
| [`inbound`](/spec/glue#inbound-webhooks) | a webhook that creates records |
59+
| [`outbound`](/spec/glue#outbound-departures-on-a-queue-or-a-topic) | a record emitted on a queue or a topic when an event fires |
5860
| [`rollups`](/spec/glue#rollups-denormalised-parent-totals) | counts, sums, balance + status maintenance |
5961
| [`settlements`](/spec/glue#settlements-payment-allocation) | auto-allocation of payments across open invoices |
6062
| [`expansions`](/spec/glue#expansions-child-rows-from-a-date-span) | generated child rows per day / week / month |

docs/spec/glue.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: Declarative glue
33
description: notifications, schedules, integrations, inbound arrivals (webhook, message, file), roll-ups, keyed aggregates, settlements, expansions, generates, transitions, postings and event-driven row posting - declared in the intent, generated as integration code, never hand-written.
4+
description: notifications, schedules, integrations, inbound webhooks, outbound departures, roll-ups, keyed aggregates, settlements, expansions, generates, transitions, postings and event-driven row posting - declared in the intent, generated as integration code, never hand-written.
45
---
56

67
# Declarative glue
@@ -346,6 +347,48 @@ That is why a `folder` source requires its `cron` (and why a `cron` is an error
346347

347348
Conversation-shaped transports — acknowledgements, retries with backoff, certificates — stay [beyond the boundary](/spec/#the-scope-boundary): they have state and failure semantics no one-line declaration should pretend to carry.
348349

350+
## outbound — departures on a queue or a topic
351+
352+
The mirror of `inbound`: the application **raises a business event** for something outside it, on a message channel rather than over HTTP.
353+
354+
```yaml
355+
outbound:
356+
# the record's own representation, on a queue — one consumer takes each message
357+
- { name: publishOrder, event: { onCreate: Order }, to: { queue: "orders.outbound" } }
358+
359+
# a declared envelope, on a topic — every subscriber receives it
360+
- name: announceActivation
361+
event: { onUpdate: Order, when: "channel != internal" }
362+
to: { topic: "order-activations" }
363+
payload:
364+
type: "order.activated"
365+
version: 1
366+
messageId: "{uuid}"
367+
tenantId: "{tenant}"
368+
reference: number
369+
customer: customer.name
370+
```
371+
372+
`to:` names the channel; `payload:` is the same [declared envelope](#payload-the-declared-envelope) an integration sends, and without it the body is the record as stored — exactly what an integration forwards today.
373+
374+
Use `integrations` when you are calling another system's API and want its answer; use `outbound` when you are announcing that something happened and nobody answers. The two are separate constructs rather than one with a transport switch precisely because their failure semantics differ: a failed call is a failed call, a failed announcement is a missed announcement.
375+
376+
::: info Normative
377+
An entry MUST declare **exactly one** of `queue` / `topic`. Two channels are two departures wearing one name; none is a promise with nowhere to land — both MUST be reported as authoring errors, mirroring the arrival rule.
378+
379+
A departure binds to the same event axis as every other reacting block, and takes the same `when:` guard.
380+
381+
The message MUST be published **after** the write that raised the event is persisted, and MUST NOT be transactional with it: a failure MUST be recorded and MUST NOT fail that write.
382+
383+
Ordering, exactly-once delivery and an outbox are NOT promised. A conforming implementation MUST state this rather than leave an author to assume otherwise — an author who believes there is an outbox writes a different application than one who knows there is none.
384+
385+
An implementation MUST NOT add fields to a declared payload that the author did not name: the point of declaring the envelope is that adding a column does not change what leaves.
386+
:::
387+
388+
A destination name in the model is a name the *application* owns. Whether two separate deployments sharing one broker can meet on it is a property of the implementation's isolation between tenants, not of the format — so an implementation that renames destinations per tenant MUST document how an author declares a destination that **is a contract with someone else**, or a departure quietly means less than it reads.
389+
390+
Conversation-shaped transports — acknowledgement protocols, request-reply correlation, backoff policy — stay [beyond the boundary](/spec/#the-scope-boundary), as they are for an integration.
391+
349392
## rollups — denormalised parent totals
350393

351394
```yaml

0 commit comments

Comments
 (0)