Once a system is more than one process, how the parts talk becomes an architectural decision. Synchronous vs. asynchronous, the protocols, and the gateway in front.
Why: the biggest communication decision is synchronous (the caller waits for a response — simple, but couples availability and can cascade failures) vs. asynchronous (the caller sends a message/event and moves on — resilient and decoupled, but eventually consistent). When: use sync for request/response where you need an answer now; use async for work that can happen later or fan out to many consumers. Where: over-using synchronous calls between services is what creates fragile, cascading distributed systems.
SYNCHRONOUS (request/response — REST, gRPC):
A ──call──► B A WAITS for B. Simple, immediate.
- A's uptime now depends on B's; slow/failing B cascades to A.
ASYNCHRONOUS (messages/events — queue, event stream):
A ──event──► [ broker ] ──► B (later) A doesn't wait.
+ decoupled, resilient, absorbs load spikes
- eventually consistent, harder to trace
Rule: sync when you need the answer NOW; async for everything that can wait.Why: within synchronous communication the common choices are REST (simple, ubiquitous, great for public APIs), gRPC (fast binary RPC, strongly typed — ideal for internal service-to-service), and GraphQL (clients request exactly the fields they need); async uses message brokers and event streams. When: REST for external/public APIs, gRPC for high-performance internal calls, GraphQL for flexible client-driven reads, a broker for events. Where: the right protocol per interaction matters more than picking one everywhere.
SYNCHRONOUS:
REST simple, cacheable, universal -> public APIs (rest-api course)
gRPC fast binary, strongly typed -> internal service-to-service
GraphQL client asks for exact fields -> flexible client reads (graphql course)
ASYNCHRONOUS:
message queue point-to-point work distribution
event stream durable log, many consumers, replay (Kafka)
Match the protocol to the interaction; don't force one everywhere.Why: exposing many services directly to clients is messy and insecure, so an API gateway sits in front as a single entry point — routing requests, and centralizing cross-cutting concerns like authentication, rate limiting, and TLS. When: put a gateway in front of a microservices backend so clients see one API and services stay internal. Where: it also decouples clients from the internal service topology, which can then change freely.
┌──► [ Orders svc ]
clients ──► [ API GATEWAY ] ──► [ Payments svc ]
│ single entry └──► [ Shipping svc ]
│ point
└─ centralizes: auth, rate limiting, TLS, routing, request shaping
Clients see ONE API; services stay internal and can change topology freely.
(Cross-cutting concerns live at the gateway, not duplicated in every service.)