1.Producers, consumers — and why a topic is not a queue
The section draws one shape: Django emits a domain event, a broker carries it, and several consumers — billing, notification, analytics, search — react independently. The producer names *what happened* (`order.placed`) rather than what should be done, and does not know who is listening. The distinction that matters most is queue versus topic. A **queue** delivers each message to exactly one consumer, which is how you distribute work. A **topic** delivers each message to every subscriber, which is how you fan out to four systems that each need it.
Key features
- Events name facts in the past tense (`order.placed`), never actions (`send_email`)
- Queue: each message goes to exactly one consumer — work distribution
- Topic: each message goes to every subscriber — fan-out
- The mature shape is both: one topic → several subscriptions, each a queue for a worker pool
- Django signals are synchronous and in-process — a decoupling tool, not an event bus
- Payload = the id plus the few fields a consumer needs to decide whether it cares
Queue or topic — and what going wrong looks like
Task, signal or event — three things that are easy to conflate
- Django emits order.placed
- Broker — one topic, four subscriptions — each subscription is itself a queue, shared by that consumer's worker pool
- billing.order-placed — 3 workers share this queue
- notification.order-placed — 2 workers share this queue
- analytics.order-placed — 1 worker
- search.order-placed — 2 workers share this queue
- Consumers — independent, and each may fail alone — adding a fifth changes nothing in Django
- Billing — raises the invoice
- Notification — sends the confirmation
- Analytics — appends to the warehouse
- Search — reindexes the order
Command
publish("order.placed", {"order_id": order.id, "total": str(order.total)})Publish facts in the past tense (`order.placed`), not commands — a command puts every consumer's decision back in the producer. A queue delivers each message to one consumer (work distribution); a topic delivers to every subscriber (fan-out), and the mature shape is one topic feeding several subscriptions that are each a queue for one worker pool. Choosing wrongly in the fan-out direction is the dangerous one, because every message *is* processed and nothing looks broken while three of four systems never run. Django signals are synchronous and in-process — decoupling within a project, never an event bus.