Skip to content

ADR-0002. In-process WebSocket-only eventing, no Kafka

FieldValue
StatusAccepted
Date2026-05-01
Decidersoutreach-team
Supersedes

Context

  • Most BANA services emit Kafka events for CDC and cross-service choreography.
  • Outreach captures inquiries and subscriptions from the public site. The only downstream consumer is the BO dashboard, which wants a real-time toast when a new inquiry arrives.
  • No other service needs to react to an inquiry or subscription as a domain event. The durable record is the row in PostgreSQL.

Decision

Outreach ships no Kafka producer and no Kafka consumer, and no BullMQ worker. Its sole async surface is a Redis-backed WebSocketEmitter (outreach-ws-emitter) that broadcasts INQUIRY_SUBMITTED to observation rooms when an inquiry is submitted.

The emission is fire-and-forget: the inquiry is committed to the database first, then OutreachSocketEventService.notifyInquirySubmitted() runs without being awaited, guarded by isReady() and using Promise.allSettled across rooms. A dropped WS event never loses data — the BO can re-fetch via GET /inquiries.

Consequences

ProsCons
Minimal infra: no Kafka topic, schema, consumer group, or DLQ to operateIf a future service needs inquiry events, it must poll REST or a Kafka seam must be added
Real-time BO UX via Redis fan-out (works across replicas)WS delivery is best-effort, not durable
Simpler service: fewer failure modes, smaller blast radiusInconsistent with the Kafka-CDC norm elsewhere in the platform
Database remains the single source of truthAnalytics on inquiries must read the DB, not an event stream

Alternatives Considered

OptionProsConsWhy rejected
Emit Kafka inquiry.submittedDurable, fan-out to any consumer, fits platform normOperational weight (topic, consumer, DLQ) for one in-house consumerNo durable consumer exists today
Debezium CDC on the Inquiry tableZero app code; captures all writesHeavy infra for a low-traffic table; couples consumers to schemaDisproportionate to need
Await the WS notify in the requestGuarantees notify before 201Couples submit latency/availability to Redis healthSubmit must succeed even if Redis is down

References

  • packages/outreach/src/components/websocket/component.ts (ApplicationWebSocketComponent)
  • packages/outreach/src/components/websocket/socket-event.service.ts (notifyInquirySubmitted)
  • packages/outreach/src/application.ts (no Kafka/BullMQ components configured)
  • API Events

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.