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

2.9 KiB

broker — multi-host fan-out contract

(no submodules)

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

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

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.