Skip to content

feat(broker): broker:provision-topics deploy script + provisionTopic (EVO-1200)#31

Merged
dpaes merged 3 commits into
developfrom
feat/EVO-1200
Jun 9, 2026
Merged

feat(broker): broker:provision-topics deploy script + provisionTopic (EVO-1200)#31
dpaes merged 3 commits into
developfrom
feat/EVO-1200

Conversation

@nickoliveira23

Copy link
Copy Markdown

Summary

Explicit deploy-time topic provisioning (story 1.7) so production no longer relies on lazy runtime topic creation. Adds a public provisionTopic to the broker abstraction and a one-shot script that provisions the 7 canonical pipeline topics through the active adapter. Closes Epic 1 (Foundation).

  • IMessageBroker.provisionTopic(topic) — idempotent. Kafka: admin.createTopics (TOPIC_ALREADY_EXISTS ignored). RabbitMQ: durable topic exchange + a default durable queue, declared but not bound (consumers bind their own ${runMode}-${topic} queue on subscribe; a bound default queue would accumulate a copy of every message).
  • scripts/broker-provision-topics.ts + npm run broker:provision-topics — boots a minimal Nest context with BrokerModule, provisions ALL_CONTRACT_TOPIC_NAMES + the events.received template root (7 topics), bounded graceful shutdown then exits.
  • README: when/how to run it in the deploy flow.

Security

  • No secrets; broker connection via existing env/config. Read-only beyond idempotent topology creation.

Test plan

  • evo-flow: npm run typecheck / npm run lint — clean
  • evo-flow: npm test -- src/shared/broker/adapters — 48 unit specs pass (incl. provisionTopic for both adapters; RabbitMQ asserts the queue is declared unbound)
  • evo-flow: BROKER_TYPE=kafka npm run broker:provision-topics — 1st + 2nd run both "Provisioned 7 topics", exit 0 (idempotent)
  • evo-flow: BROKER_TYPE=rabbitmq npm run broker:provision-topics — same; verified 7 exchanges + 7 queues, no topic→queue bindings

Changed Files

  • src/shared/broker/interfaces/message-broker.interface.ts
  • src/shared/broker/adapters/{kafka,rabbitmq}-broker.adapter.ts (+ specs)
  • scripts/broker-provision-topics.ts
  • package.json, src/shared/broker/README.md

QA Notes (self-review, fixed)

  • MEDIUM: a bound provisioned default queue would accumulate undrained copies (disk fill) → switched to declaring the queue unbound.

Notes

Linked Issue

  • EVO-1200

🤖 Generated with Claude Code

nickoliveira23 and others added 3 commits June 8, 2026 18:15
Public, idempotent topic provisioning: Kafka via admin.createTopics
(TOPIC_ALREADY_EXISTS ignored); RabbitMQ via assertExchange + a default durable
queue bound with `<topic>.#` (so the events.received exchange also catches the
dynamic per-platform routing keys). Touches src/shared/broker/** — guarded by
the contract-suite gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Standalone script boots a minimal Nest context with BrokerModule and provisions
the 7 canonical topics through the active adapter. Idempotent; bounded graceful
shutdown then force-exit (the broker client keeps the loop alive otherwise).
README documents when to run it in the deploy flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ion (EVO-1200)

Self-review MEDIUM: binding the provisioned default queue to its topic exchange
would make it accumulate a copy of every message (the real consumer uses its
own ${runMode}-${topic} queue), filling the broker disk in production. Declare
the durable queue but do not bind it — consumers bind on subscribe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @nickoliveira23, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@dpaes dpaes left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — Approved ✅

Read both adapters, the interface, the deploy script, the topic contracts, and the new specs.

ACs:

  • AC1 (Kafka, 7 topics) — MET. Script provisions [...ALL_CONTRACT_TOPIC_NAMES (6), 'events.received'] = 7 via admin.createTopics.
  • AC2 (RabbitMQ, 7 exchanges + default durable queues) — MET. The crux safety call is correct: ensureExchange + assertQueue(durable:true) with no bindQueue — the default queue is declared but intentionally unbound, so it can't accumulate undrained copies and fill disk. The only bindQueue in the file is attachConsumer binding the consumer's own ${runMode}-${topic} queue on subscribe. The spec asserts bindQueue is NOT called.
  • AC3 (idempotent re-run) — MET by adapter logic (Kafka swallows only /already exists/i and caches; RabbitMQ assert* are AMQP no-ops + declaredExchanges guard). The Kafka catch is correctly scoped — every non-"already exists" error rethrows, so a real provisioning failure (auth/connect/invalid config) propagates to process.exit(1), no false-green. Connection lifecycle is bounded (Promise.race([app.close(), 5s]) + forced exit), so no hung deploy step.

Non-blocking notes (LOW/NIT):

  • IMessageBroker.provisionTopic JSDoc still says RabbitMQ exchange + default durable queue + binding — that + binding contradicts the deliberate no-bind design (impl + README + spec all confirm no binding). Worth dropping + binding from the JSDoc so a future maintainer doesn't "fix" the adapter and reintroduce the disk-fill bug.
  • AC3 re-run is correct but exercised by no test (the new specs are single-call topology only); events.enriched / bare events.received provision inert/orphan topology; Kafka matches "already exists" by message string, not error code.

CI caveat: evo-flow runs only Sourcery (no jest gate), so "specs pass" is author-self-reported. The specs are well-formed and would prove the topology if run — worth a local npm test.

Merging to develop.

@dpaes dpaes merged commit f2bf88a into develop Jun 9, 2026
4 checks passed
@dpaes dpaes deleted the feat/EVO-1200 branch June 9, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants