Skip to content

TMS: API with DISABLE_EMBEDDED_WORKERS=true delivers no domain events to any handler #327

Closed

Description

@Connor-Finn

Problem

PgBossEventBus.publish records each event in DomainEventLog, then sends it only to queues for handlers registered in the same process (fanOut walks its local registry). When the API runs with DISABLE_EMBEDDED_WORKERS=true, the documented setup for a separate worker container (docs/SELF_HOSTING.md), index.ts never calls registerEventHandlers. So the API's registry is empty, and every event its commands publish is recorded and delivered nowhere.

In that setup, nothing done through the API reaches:

  • projections: shipment/order read models, list views, timeline
  • audit, notifications, email, customer webhooks, triage
  • EDI auto-send and billing triggers

The standalone worker can't pick them up either, because nothing is ever put on the evt.* queues.

The default docker-compose.yml doesn't set the flag, so the API still runs its own handlers there and isn't affected. Any deployment that follows the separate-worker docs is affected.

Suggested fix

Make fan-out independent of who consumes. The API needs to know every handler's queue name and patterns without consuming from them. For example, registerEventHandlers could register routing for each handler on every process, with eventBus.start() (which subscribes to queues) only called where handlers should run. An alternative is a static routing table built from the handler list.

Acceptance criteria

  • With DISABLE_EMBEDDED_WORKERS=true on the API and a WORKER_MODE=all worker, creating a shipment through the API updates ShipmentReadModel and the timeline.
  • No handler runs twice when the API has embedded workers enabled.
  • A test covers fan-out from a process that registers routing but doesn't consume.

Related: found while fixing #287.

Activity

  1. Connor-Finn commented on Oct 4, 2026

    @Connor-Finn
    CollaboratorAuthor

    Related gap found while doing #328: handlers that dispatch commands aren't registered in two setups.

    • Embedded workers (API process, the default dev setup): index.ts calls registerEventHandlers without a command bus, so every handler under if (commandBus) in events/registerHandlers.ts is skipped. That includes IssueEngineHandler and AutoReplenishmentHandler, and the triage agent even with an LLM configured.
    • Standalone worker: worker.ts only builds its command bus when an LLM provider is configured and enabled, so without one the same handlers are skipped there too.

    #336 works around it for route planning only (registerShipmentRoutePlanning, called from index.ts with the container's command bus). The general fix probably belongs with this issue's rework of handler registration: always pass a command bus, in both processes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    TMSTransport management

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions