Skip to content

ADR-0001. In-process EventBus + BullMQ for eventing (no Kafka)

FieldValue
StatusAccepted
Date2026-04-01
Deciderssupport-team
Supersedes

Context

  • Helpdesk needs to react to domain events (ticket created, status changed, assigned, message added) and run heavy work asynchronously (assignment, SLA monitoring, escalation, notifications, context enrichment, surveys).
  • The platform standard for cross-service eventing is Kafka. @platformatic/kafka is even declared in package.json.
  • However, all of helpdesk's event flows are intra-service: an event emitted by a use-case is consumed by a listener in the same service that fans out to background jobs. No other service subscribes to helpdesk events, and helpdesk subscribes to no external topics.

Decision

Use a two-tier in-service mechanism and no Kafka:

  1. In-process EventBus (eventemitter3, EventBusComponent) — use-cases emit TicketEventTypes events via TicketEmitter; listeners in src/application/events/listeners/ handle them synchronously within the API process. Handler errors are caught and logged, never crashing the emitter.
  2. BullMQ queues (QueueComponent, WorkerComponent) — listeners fan out to durable Redis-backed queues; a separate worker process (RUN_MODE=worker) consumes them with per-queue retry/backoff.

@platformatic/kafka remains declared but unused (dead dependency).

Consequences

ProsCons
No Kafka cluster dependency for a self-contained serviceEvents are not externally observable — other services cannot subscribe
Synchronous emit keeps HTTP responses fast (work deferred to BullMQ)EventBus is at-most-once; a crash between emit and enqueue loses the fan-out
BullMQ gives durable retries + DLQ without a brokerTwo delivery models (in-process + queue) to reason about
Simpler local dev (only Redis needed)Dead @platformatic/kafka dep misleads readers and bloats install

Alternatives Considered

OptionWhy rejected
Kafka for all eventingOver-engineered for intra-service flows; adds broker dependency with no external subscriber
BullMQ only (no in-process bus)Forces every domain reaction through Redis even when synchronous in-process handling suffices
Transactional outbox + CDCHeavy for a service with no external event consumers

References

  • src/components/event-bus/event-bus.ts (eventemitter3)
  • src/components/event-bus/event-registry.ts
  • src/application/events/emitters/ticket.emitter.ts
  • src/components/queue.component.ts
  • package.json (@platformatic/kafka declared, unused)

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