Files
skills/docs/api-stack/api/api-ws/src/broker/broker.md
2026-05-29 08:20:56 +01:00

82 lines
2.9 KiB
Markdown

# `broker` — multi-host fan-out contract
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
A single-host deployment never needs a broker — the `SubscriptionBus`
inside that process already sees every connection. Production deployments
that run multiple host replicas behind a load balancer DO need one: a
broadcast issued on host A has to reach the connections attached to host
B without migrating connections between processes. The `WsBroker` contract
defined here is the seam those broker adapters implement.
Concrete brokers (`api-ws-broker-in-memory` for single-host tests,
`api-ws-broker-redis` for production) plug behind this interface and stay
out of plugin code paths.
## Features
- `BrokerEnvelope` — wire payload (`topic`, `tenant`, `payload`, optional
`originHost`).
- `BrokerHandler` — subscriber callback signature.
- `BrokerSubscription` — disposal handle returned from `subscribe`.
- `WsBroker<Payload>` interface — `publish`, `subscribe`, `dispose`. Each
method may return synchronously or asynchronously so in-memory brokers
stay cheap and Redis-backed brokers can `await` round trips.
## Usage: Basic
```ts
import type { WsBroker } from "@suaveplan/api-ws/broker";
async function fanOut(broker: WsBroker<{ orderId: string }>): Promise<void> {
await broker.publish("orders.created", {
topic: "orders.created",
tenant: tenantId,
payload: { orderId: "o1" },
});
}
```
## Usage: Advanced
```ts
import type { WsBroker, BrokerSubscription } from "@suaveplan/api-ws/broker";
async function bridge(
broker: WsBroker<unknown>,
local: { broadcast: (...args: unknown[]) => void },
): Promise<BrokerSubscription> {
const subscription = await broker.subscribe("orders.created", (envelope) => {
local.broadcast(envelope.topic, {
tenant: envelope.tenant,
payload: envelope.payload,
});
});
return subscription;
}
```
## API Reference
- `interface BrokerEnvelope<Payload> { topic, tenant, payload, originHost? }`.
- `type BrokerHandler<Payload> = (env: BrokerEnvelope<Payload>) => void | Promise<void>`.
- `interface BrokerSubscription { topic; unsubscribe(): void | Promise<void> }`.
- `interface WsBroker<Payload> { publish, subscribe, dispose }`.
## Implementation Notes
- The contract intentionally does not specify reliability semantics. Each
adapter declares its own delivery guarantees (`in-memory` is at-most-
once with FIFO inside the process; `redis` is at-most-once with Redis
pub/sub semantics). Plugins that need stronger guarantees use the
saga / 2PC capabilities elsewhere in the foundation.
- `originHost` is optional so adapters that don't need loop-back
suppression can omit it. Adapters that DO suppress loop-back compare
against the local host's identity at the receive side.
- `dispose` MUST close every subscription cleanly; the conformance
harness pins this invariant against every concrete adapter.