Skip to content
DocumentationProject HTTP, streams & messaging
On this page

Page resources

Open Markdownllms.txtView source

Last updated

This recipe exposes owned operations through HTTP, a resumable stream, and a message queue without turning routes, status codes, frames, or broker acknowledgement into domain meaning.

The .one syntax below is illustrative end-product syntax.

1. Start with operations

One
semantic parcelhub.tracking
    domain one.contracts@1
    fault TrackingFault@1
        not_found
    operation Tracking.GetShipment@1
        accepts GetShipmentRequest
        returns Shipment
        faults TrackingFault
        effect read

    operation Tracking.WatchShipment@1
        accepts WatchShipmentRequest
        returns stream<ShipmentEvent>
        delivery resumable cursor: ShipmentCursor
        ordering per_shipment
        backpressure bounded(256)

Neither operation implies a network boundary.

2. Add an HTTP projection

One
semantic parcelhub.tracking_http
    domain one.communication.http@1
    interface TrackingHttp@1
        operation parcelhub.tracking#Tracking.GetShipment@1
            request GET "/shipments/{shipment_id}"
            parameter shipment_id from input.shipment_id
            result 200 json(output)
            fault TrackingFault.not_found -> 404 json(fault)
        operation parcelhub.tracking#Tracking.WatchShipment@1
            request GET "/shipments/{shipment_id}/events"
            result server_sent_events(output)
            resume header("Last-Event-ID") from input.cursor

The profile validates path collisions, encoding, headers, status mappings, identity, deadlines, cancellation, disclosure, and semantic loss.

3. Consume the stream with explicit flow control

Rust
let mut events = client
    .watch_shipment(WatchShipmentRequest { shipment_id, cursor })
    .await?;

while let Some(event) = events.next().await? {
    apply(event.value)?;
    events.ack(event.cursor).await?;
}

A gap, expired cursor, slow reader, revocation, transport loss, and domain end remain different dispositions. Reconnect resumes from an accepted cursor; it does not replay a mutation.

4. Test the claimed boundary

Console
one test --root TrackingHttp --scenario canonical-paths
one test --root TrackingHttp --scenario stream-gap-and-resnapshot
one test --root TrackingHttp --scenario slow-reader-backpressure

See communication.

5. Project commands and events to messaging

The same semantic edges project to a queue. Broker acknowledgement is delivery evidence, not the domain result:

One
operation Orders.Place@1
    accepts PlaceOrderRequest
    returns PlaceOrderResponse
    faults OrdersPlaceFault
    effect irreversible
    idempotency operation(window: 24h)

event OrderPlaced@1
    payload OrderPlacedEvent
    ordering customer
    retention 7d
One
interface OrdersMessaging@1
    command parcelhub.orders#Orders.Place@1
        channel "orders.place"
        key input.customer_id
        delivery at_least_once
        acknowledgement accepted_for_delivery

    event parcelhub.orders#OrderPlaced@1
        channel "orders.placed"
        key payload.customer_id
        cursor broker_offset

The profile owns envelope, key, acknowledgement, retry, dead-letter, deduplication, ordering, cursor, and fault mappings. The consumer preserves the distinction between an applied effect, a domain rejection, an unknown outcome, and a retryable cause:

Rust
match delivery.handle().await {
    Applied(receipt) => delivery.ack(receipt).await?,
    Rejected(fault) => delivery.reject(fault).await?,
    OutcomeUnknown(effect) => delivery.suspend(effect).await?,
    Retryable(cause) => delivery.retry_within_budget(cause).await?,
}

See handle failures.