Communication Between Services

Lesson 1 of 10 · 14 min

x
10%

Why Services Communicate

In a monolith, modules call each other through in-process function calls — fast, simple, and transactional. In a distributed system, each service runs in its own process (often its own container or VM), so calling another service means crossing a network boundary. That boundary introduces latency, partial failures, and the need for explicit contracts. Service communication falls into two broad families. Synchronous communication blocks the caller until the callee responds — like REST over HTTP or gRPC. Asynchronous communication sends a message and moves on — the receiver processes it later via a queue or event bus. Neither is universally better; the choice depends on whether the caller needs an immediate answer, whether failures should block the workflow, and how tightly coupled the services should be.

Before
Monolith — in-process call
// One process, one database
function placeOrder(userId: string, items: Item[]) {
  const user = userRepo.findById(userId);
  const order = orderRepo.create({ userId, items });
  emailService.sendConfirmation(user.email, order);
  return order;
}
After
Microservices — network calls
// Three separate services over HTTP
async function placeOrder(userId: string, items: Item[]) {
  const user = await userService.get(userId);
  const order = await orderService.create({ userId, items });
  await notificationService.sendConfirmation(user.email, order);
  return order;
}

Key Takeaway

Every cross-service call is a network hop — design for latency, failure, and loose coupling from day one.

Next Lesson