Preskoči na vsebino

Med-orbitalna komunikacija

Source: tests/schemas/05-cross-orbital.orb

Orbitals are self-contained — but real applications need features to talk to each other. Orb connects orbitals through a typed event bus: one orbital emits, others listen.

OrderLifecycleFulfillment/orders/track
Orbital Unit = Entity + Traits + Pages

Vzorec

CartManager orbital          NotificationManager orbital
| |
CartActions trait NotificationHandler trait
| |
emits: ITEM_ADDED ──────────► listens: ITEM_ADDED
emits: CART_CLEARED ─────────► listens: CART_CLEARED

The key properties:

  • emits — declared on a trait and on the orbital (what events it publishes)
  • listens — declared on a trait (which events it reacts to) and on the orbital (which orbitals it subscribes to)
  • scope: "external" — marks an event as crossing orbital boundaries

Korak 1 — Declare Emits on the Emitting Trait

The trait declares what events it can publish, including the payload contract:

{
"ime": "CartActions",
"povezanaEntiteta": "Cart",
"kategorija": "interaction",
"oddaja": [
{
"dogodek": "ITEM_ADDED",
"obseg": "external",
"opis": "Emitted when an item is added to cart",
"podatki": [
{
"ime": "itemCount",
"tip": "stevilo",
"obvezno": true
},
{
"ime": "total",
"tip": "stevilo",
"obvezno": true
}
]
},
{
"dogodek": "CART_CLEARED",
"obseg": "external",
"opis": "Emitted when cart is cleared",
"podatki": [
{
"ime": "timestamp",
"tip": "stevilo",
"obvezno": true
}
]
}
],
"strojStanj": {
"...": "..."
}
}

scope: "external" is required for cross-orbital events. Without it, the event stays internal to the trait.


Korak 2 — Fire the Event in a Transition

Inside a transition's effects, use ["emit", "EVENT_NAME", payload]:

{
"iz": "empty",
"dogodek": "ADD_ITEM",
"do": "hasItems",
"ucinki": [
[
"increment",
"@entiteta.itemCount",
1
],
[
"nastavi",
"@entiteta.total",
[
"plus",
"@entiteta.total",
"@podatki.price"
]
],
[
"oddaj",
"ITEM_ADDED",
{
"itemCount": "@entiteta.itemCount",
"total": "@entiteta.total"
}
]
]
}

The payload is a JSON object where values can be bindings (@entity.*) or literals.


Korak 3 — Declare Orbital-Level Emits

At the orbital level, list every event the orbital publishes:

{
"ime": "CartManager",
"entiteta": {
"...": "..."
},
"znacilnosti": [
{
"...": "..."
}
],
"strani": [
{
"...": "..."
}
],
"oddaja": [
"ITEM_ADDED",
"CART_CLEARED"
]
}

Korak 4 — Declare Listens on the Receiving Trait

The receiving trait declares which external events it handles:

{
"ime": "NotificationHandler",
"povezanaEntiteta": "Notification",
"kategorija": "interaction",
"poslusa": [
{
"dogodek": "ITEM_ADDED",
"obseg": "external"
},
{
"dogodek": "CART_CLEARED",
"obseg": "external"
}
],
"strojStanj": {
"...": "..."
}
}

These events become valid event keys in the state machine — add them to events and write transitions for them:

"events": [
{ "key": "INIT", "name": "Initialize" },
{ "key": "ITEM_ADDED", "name": "Item Added" },
{ "key": "CART_CLEARED", "name": "Cart Cleared" }
],
"transitions": [
{
"from": "idle",
"event": "ITEM_ADDED",
"to": "notified",
"effects": [
["increment", "@entity.count", 1],
["set", "@entity.message", "Item added to cart"]
]
},
{
"from": "notified",
"event": "CART_CLEARED",
"to": "idle",
"effects": [
["set", "@entity.message", "Cart cleared"],
["set", "@entity.count", 0]
]
}
]

Korak 5 — Declare Orbital-Level Listens

At the receiving orbital level, declare which orbital the events come from:

{
"ime": "NotificationManager",
"entiteta": {
"...": "..."
},
"znacilnosti": [
{
"...": "..."
}
],
"strani": [
{
"...": "..."
}
],
"poslusa": [
{
"dogodek": "ITEM_ADDED",
"iz": "CartManager"
},
{
"dogodek": "CART_CLEARED",
"iz": "CartManager"
}
]
}

Celoten program

;; app cross-orbital-test

orbitala CartManager {
entiteta Cart [izvajanje] {
id : niz!
itemCount : stevilo
total : stevilo
}
znacilnost CartActions -> Cart [interakcija] {
zacetno: empty
stanje empty {
ZACETEK -> empty
(izrisi-vmesnik main { type: "stats", title: "Shopping Cart", value: "@entity.itemCount", subtitle: "Total: $@entity.total" })
ADD_ITEM -> hasItems
(increment @entiteta.itemCount 1)
(nastavi @entiteta.total (+ @entiteta.total @podatki.price))
(oddaj ITEM_ADDED { itemCount: "@entity.itemCount", total: "@entity.total" })
}
stanje hasItems {
ADD_ITEM -> hasItems
(increment @entiteta.itemCount 1)
(nastavi @entiteta.total (+ @entiteta.total @podatki.price))
(oddaj ITEM_ADDED { itemCount: "@entity.itemCount", total: "@entity.total" })
CLEAR -> empty
(nastavi @entiteta.itemCount 0)
(nastavi @entiteta.total 0)
(oddaj CART_CLEARED { timestamp: "@now" })
}
oddaja {
ITEM_ADDED zunanje { itemCount: stevilo, total: stevilo }
CART_CLEARED zunanje { timestamp: stevilo }
}
}
stran "/cart" -> CartActions
}
orbitala NotificationManager {
entiteta Notification [izvajanje] {
id : niz!
message : niz
count : stevilo
}
znacilnost NotificationHandler -> Notification [interakcija] {
zacetno: idle
stanje idle {
ZACETEK -> idle
(izrisi-vmesnik main { type: "stats", title: "Notifications", value: "@entity.count", subtitle: "@entity.message" })
ITEM_ADDED -> notified
(increment @entiteta.count 1)
(nastavi @entiteta.message "Item added to cart")
}
stanje notified {
ITEM_ADDED -> notified
(increment @entiteta.count 1)
CART_CLEARED -> idle
(nastavi @entiteta.message "Cart cleared")
(nastavi @entiteta.count 0)
}
poslusa {
* ITEM_ADDED -> undefined
* CART_CLEARED -> undefined
}
}
stran "/notifications" -> NotificationHandler
}

Kontrolni seznam: med-orbitalni dogodki

Use this checklist when wiring two orbitals together:

  • Emitting trait has "emits": [...] with scope: "external" and a payload contract
  • Emitting transition calls ["emit", "EVENT_NAME", {...payload}] in effects
  • Emitting orbital has top-level "emits": ["EVENT_NAME"]
  • Listening trait has "listens": [{ "event": "EVENT_NAME", "scope": "external" }]
  • Listening trait's state machine has the event in events and a transition for it
  • Listening orbital has top-level "listens": [{ "event": "EVENT_NAME", "from": "EmittingOrbital" }]

Naslednji koraki