Voice and hearing
Humans cooperate by communicating: talking face to face, writing letters, posting notices. Microservices do exactly the same, and choosing the right way to communicate decides whether your society is calm or chaotic.
Three ways to communicate
| Human way | Pattern | When to use | AWS |
|---|---|---|---|
| Talking face to face — you wait for the answer | Synchronous request/response | You need the answer now to continue | HTTP APIs (API Gateway, function URLs) |
| Writing a letter to one person | Asynchronous point-to-point messaging | A specific human must do some work, eventually, reliably | SQS |
| Posting on the town notice board | Publish/subscribe events | Something happened; anyone interested may react | EventBridge, SNS |
Commands vs events
- A command asks someone to do something: "Cook order 42." It has one intended receiver. It might be refused.
- An event states a fact about the past: "Order 42 was placed." The speaker doesn't know or care who listens.
Events make a society loosely coupled: the Waiter announces OrderPlaced; the Chef, Cashier and Analyst each decide what to do. Adding a new listener (say, a loyalty-points service) requires no change to the Waiter.
The voice: publishing events
// voice/event-voice.ts — announce facts on the town notice board (EventBridge)
import { EventBridgeClient, PutEventsCommand } from "@aws-sdk/client-eventbridge";
import type { Voice } from "../brain/place-order";
const bus = new EventBridgeClient({});
export const voice: Voice = {
async announce(type, detail) {
const res = await bus.send(new PutEventsCommand({
Entries: [{
EventBusName: process.env.EVENT_BUS_NAME,
Source: "cafe.waiter",
DetailType: type, // e.g. "OrderPlaced"
Detail: JSON.stringify(detail),
}],
}));
if (res.FailedEntryCount) throw new Error("Could not announce event");
},
};
Speak clearly: event contracts
An event is a promise to every listener. Give each one:
- A clear name in the past tense (
OrderPlaced,PaymentFailed). - A documented schema (EventBridge has a schema registry).
- A version strategy — add fields freely; never remove or rename fields others rely on without a new version.
- Enough data for listeners to act without calling back (but not your whole memory).
Hearing: subscribing to others
The Chef listens for OrderPlaced. In AWS, an EventBridge rule matches the event and delivers it — ideally into the Chef's own SQS queue (an inbox), so bursts are buffered and failures retried.
Waiter ── OrderPlaced ──► EventBridge bus ──rule: source=cafe.waiter, type=OrderPlaced──► SQS chef-inbox ──► Chef Lambda
Hearing things twice: idempotency
Distributed systems deliver messages at least once — occasionally twice. A good listener isn't confused by repetition: hearing "cook order 42" twice must not cook two meals. This property is called idempotency.
Techniques:
- Use a natural unique ID (the order ID) and a conditional write that only succeeds the first time.
- Keep a short-lived "already processed" record with a TTL (Powertools for AWS Lambda has an idempotency utility that does this with DynamoDB).
// The Chef records that it has started an order; a duplicate message fails the condition and is skipped.
await db.send(new PutCommand({
TableName: process.env.TABLE_NAME,
Item: { pk: `TICKET#${orderId}`, sk: "TICKET", status: "COOKING" },
ConditionExpression: "attribute_not_exists(pk)",
}));
Hearing things out of order
OrderCancelled might arrive before OrderPlaced is processed. Listeners should check the current state (or timestamps/versions) before acting, rather than assuming a perfect sequence.
When talking face to face is the right choice
Synchronous calls are fine when the caller truly needs the answer immediately — checking a price, validating a coupon. But each synchronous dependency is a risk: if the other human is slow or sick, you suffer too. Keep chains short (avoid A → B → C → D waiting on each other), and use timeouts.
Talk when you need an answer now; write letters for work that must happen reliably; announce events for facts others may care about. Most healthy societies announce far more than they talk.