Living Systems Handbook microservices as humans Bipin Singh
Anatomy of one human

Voice and hearing

2 min readChapter 08 of 34By Bipin Singh

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

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:

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:

// 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.

Key idea

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.

Bipin Singh
Written by Bipin Singh

Senior Full-Stack Engineer · AI & AWS. I design and build serverless systems on AWS — and love explaining them simply.

Work with me