Um momento
0x40Lesson 5 of 15

Asynchronous messaging and events

Decouple services with events and message brokers, and handle duplicates and ordering safely.

30 min 7-question quiz 2 code exercises
By the end of this lesson you can
  • Tell commands from events, and queues from publish/subscribe
  • Explain how brokers decouple services in time and availability
  • Write idempotent consumers and keep related messages in order with partition keys

When an order is placed, the Kitchen should start cooking, Notifications should text the customer, and Analytics should count it. If Ordering called all three synchronously, a slow Notifications service would slow down every order, and adding Analytics would mean changing Ordering.

Instead, Ordering publishes an event - OrderPlaced - to a message broker (Kafka, RabbitMQ, a cloud queue). Every interested service subscribes and reacts in its own time. Ordering doesn’t know or care who’s listening.

  • A command asks one specific service to do something: CookOrder. It may fail or be rejected.
  • An event announces that something already happened: OrderPlaced. Any number of services may react - or none.
  • A queue delivers each message to one consumer (work sharing); publish/subscribe delivers each event to every subscriber group.

Try it

Follow an OrderPlaced event

Step through what happens after a customer orders. Predict before each reveal.

Message 1 of 6Predicted 0/0
Ordering
Broker
Kitchen
Notifications

Duplicates and ordering

Brokers almost always give at-least-once delivery: a message may arrive more than once (and “exactly-once” usually means “at-least-once plus deduplication”). So consumers must be idempotent - processing a message twice has the same effect as once. The usual way: give every message a unique ID and record which IDs you’ve handled, in the same transaction as the work.

Order matters too: “OrderPlaced” must be handled before “OrderCancelled” for the same order. Kafka keeps order within a partition, and messages with the same key always go to the same partition - so use the order ID as the key. Messages for different orders may interleave, which is fine.

idempotent_consumer.py
1stock = {"miso": 10}
2processed = set()
3
4def handle(message):
5    if message["id"] in processed:
6        return "duplicate, skipped"
7    stock[message["dish"]] -= message["quantity"]
8    processed.add(message["id"])           # in real life: same transaction as the update
9    return f"reserved {message['quantity']}, {stock[message['dish']]} left"
10
11for message in [{"id": "m1", "dish": "miso", "quantity": 2},
12                {"id": "m2", "dish": "miso", "quantity": 3},
13                {"id": "m1", "dish": "miso", "quantity": 2}]:
14    print(message["id"], handle(message))
Output
m1 reserved 2, 8 left
m2 reserved 3, 5 left
m1 duplicate, skipped

Key takeaways

  • Events announce facts to any number of subscribers; commands ask one service to act.

  • Brokers decouple services in time: a consumer can be down and catch up later.

  • Delivery is at-least-once, so consumers must be idempotent - track processed message IDs.

  • Key messages by entity (e.g. order ID) to keep each entity’s events in order within a partition.

Lesson quiz

7 questions · pass with 5 correct · up to 50 XP

Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.

Practice: simulate microservice patterns in Python

Build small Python simulations of the patterns - routers, sagas, outboxes, circuit breakers, traces - and run them against sample inputs. They run locally in your browser; no servers or containers needed.

Exercise 1

An idempotent inventory consumer

+25 XP

The first input line is the starting stock as dish=quantity pairs. Each following line is a message: id dish quantity. Reserve stock for each message exactly once: skip duplicate IDs (m1: duplicate), and reject a message if there isn’t enough stock (m4: rejected, only 1 tonkotsu left) - a rejected message still counts as processed. Otherwise print m1: reserved 2 miso. End with the final stock in the input’s dish order and duplicates skipped: N.

  • Friday night
main.py
Loading editor…

Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.

Exercise 2

Partition by key

+25 XP

The first input line is the number of partitions. Each following line is an event order_id event_name. Assign each event to a partition with a stable hash - the sum of the key’s UTF-8 byte values modulo the partition count - and print each partition’s events in arrival order (partition 0: 42:OrderPlaced 42:OrderPaid, or partition 1: -). Then confirm ordering: order 42 stays in partition 0 for each order, sorted by order ID.

  • Three partitions
main.py
Loading editor…

Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.

Questions about this lesson

Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.

Loading posts…

Gostou da aula? 😆👍
Apoie nosso trabalho com uma doação: