Checklist/Docs/Example: FastStream + Kafka (events)

Guide 08

Example: FastStream + Kafka (events)

Audience: Services emitting or consuming domain/integration events. Policy: Kafka for multi-consumer events; RabbitMQ remains for commands/jobs.

TL;DR

text
Producer (API or worker): after commit → outbox → Kafka topic
Consumer: FastStream subscriber → inbox → local projection / new command

Contents

  1. When Kafka vs RabbitMQ
  2. Topic design
  3. Producer pseudocode
  4. Consumer pseudocode
  5. Ordering and keys
  6. Offsets and lag
  7. Schema evolution
  8. Run

---

1. When Kafka vs RabbitMQ

Use KafkaUse RabbitMQ
Many consumers of the same factOne worker should do a job
Replay / log historyPer-message ack work queue
Stream processingClassic task queue, DLX jobs

2. Topic design

text
billing.events # domain events from billing
billing.invoice.paid # optional finer topics

Envelope:

json
{
 "event_id": "uuid",
 "type": "invoice.paid.v1",
 "occurred_at": "2026-08-05T12:00:00Z",
 "payload": { "invoice_id": "...", "customer_id": "...", "amount_cents": 1200 }
}

3. Producer pseudocode

python
# PSEUDOCODE : prefer outbox, not dual-write
async with db.transaction():
 invoice.pay()
 await db.save(invoice)
 await db.outbox_add(
 topic="billing.events",
 key=invoice.customer_id,
 payload=envelope,
 )

Relay publishes to Kafka with the key for partition affinity.

4. Consumer pseudocode

python
# PSEUDOCODE : FastStream-style
from faststream import FastStream
from faststream.kafka import KafkaBroker

broker = KafkaBroker(settings.kafka_bootstrap)
app = FastStream(broker)

@broker.subscriber("billing.events", group_id="notifications")
async def on_billing_event(message: dict):
 if message["type"] != "invoice.paid.v1":
 return
 if await inbox_seen(message["event_id"]):
 return
 await enqueue_email_job(message["payload"]["invoice_id"])
 await inbox_insert(message["event_id"])

5. Ordering and keys

  • Set key = aggregate id when order per entity matters
  • Different keys → parallel partitions
  • Do not assume global order across keys

6. Offsets and lag

  • Commit offsets after successful side effects (or use inbox so reprocessing is safe)
  • Alert on consumer lag as the primary backlog SLO
  • Scale consumers up to partition count

7. Schema evolution

  • Version in type (invoice.paid.v1v2)
  • Additive fields first; consumers ignore unknown keys
  • Document in the event catalog (11)

8. Run

bash
# consumer process
faststream run app.workers.stream:app
# or uvicorn if mounted; keep consumer separate from API when possible

Related: 11 Event-driven design · 04 Outbox