Initial Commit
This commit is contained in:
81
packages/api-stack/api/api-ws/src/broker/broker.md
Normal file
81
packages/api-stack/api/api-ws/src/broker/broker.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# `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.
|
||||
Reference in New Issue
Block a user