When you split a monolith into microservices, you trade intra-process method calls for network calls. That shift changes everything: you now deal with latency, partial failures, retries, service discovery, and distributed transactions. How services communicate is one of the most consequential architectural decisions you will make — and one of the least carefully considered.Quando você divide um monolito em microsserviços, troca chamadas de método intra-processo por chamadas de rede. Essa mudança altera tudo: agora você lida com latência, falhas parciais, retries, service discovery e transações distribuídas. Como os serviços se comunicam é uma das decisões arquiteturais mais consequentes que você tomará — e uma das menos cuidadosamente consideradas.
This article maps out the communication patterns available, when to apply each, and how to avoid the traps that turn a microservices architecture into a distributed monolith with all the downsides of both worlds.Este artigo mapeia os padrões de comunicação disponíveis, quando aplicar cada um e como evitar as armadilhas que transformam uma arquitetura de microsserviços em um monolito distribuído com todas as desvantagens dos dois mundos.
Synchronous vs. Asynchronous: The Core Trade-offSincrono vs. Assincrono: O Dilema Central
Every service-to-service interaction falls into one of two categories:Cada interação serviço-a-serviço se encaixa em uma de duas categorias:
Synchronous (Request/Response)
- Caller waits for the responseO chamador espera pela resposta
- Strong consistency guaranteesGarantias de consistência forte
- Simple to reason aboutSimples de raciocinar
- Temporal coupling — caller fails if callee is downAcoplamento temporal — chamador falha se o receptor estiver fora
- Cascading failures under loadFalhas em cascata sob carga
- Tools: REST/HTTP, gRPCFerramentas: REST/HTTP, gRPC
Asynchronous (Event/Message)
- Caller fires and forgetsO chamador dispara e esquece
- Eventual consistencyConsistência eventual
- Services are temporally decoupledServiços são desacoplados temporalmente
- Higher operational complexityMaior complexidade operacional
- Requires idempotent consumersRequer consumidores idempotentes
- Tools: Kafka, RabbitMQ, SQSFerramentas: Kafka, RabbitMQ, SQS
Decision rule: Use synchronous when the caller needs the result to continue its work (e.g., fetching user data to build a response). Use asynchronous when the caller just needs to notify that something happened and can continue regardless of what other services do with that notification.Regra de decisão: Use síncrono quando o chamador precisa do resultado para continuar seu trabalho (ex: buscar dados do usuário para construir uma resposta). Use assíncrono quando o chamador só precisa notificar que algo aconteceu e pode continuar independentemente do que outros serviços fazem com essa notificação.
REST vs. gRPCREST vs. gRPC
For synchronous communication, REST over HTTP/1.1 is the default — but it is not always the right choice. gRPC offers significant advantages in internal service-to-service communication.Para comunicação síncrona, REST sobre HTTP/1.1 é o padrão — mas nem sempre é a escolha certa. O gRPC oferece vantagens significativas na comunicação interna entre serviços.
REST: When and WhyREST: Quando e Por Que
REST remains the best choice for external-facing APIs and any scenario where human readability, broad client compatibility, or browser support matters. JSON is universally understood, tooling is mature, and debugging with curl or Postman is trivial.O REST continua sendo a melhor escolha para APIs externas e qualquer cenário onde legibilidade humana, ampla compatibilidade de clientes ou suporte a navegadores importa. JSON é universalmente compreendido, as ferramentas são maduras e depurar com curl ou Postman é trivial.
gRPC: When and WhygRPC: Quando e Por Que
gRPC uses Protocol Buffers (binary serialization) over HTTP/2 and generates strongly typed client/server stubs from a schema definition. The advantages for internal communication are measurable:O gRPC usa Protocol Buffers (serialização binária) sobre HTTP/2 e gera stubs de cliente/servidor fortemente tipados a partir de uma definição de schema. As vantagens para comunicação interna são mensuráveis:
- Performance: Protobuf payloads are 3–10x smaller than equivalent JSON. Ideal for high-frequency internal calls.Performance: Payloads Protobuf são 3–10x menores que o JSON equivalente. Ideal para chamadas internas de alta frequência.
- Streaming: Bi-directional streaming is a first-class citizen — not a workaround with WebSockets.Streaming: Streaming bidirecional é cidadão de primeira classe — não um workaround com WebSockets.
- Contract-first: The
.protofile is the contract. Client stubs are generated automatically — no manual SDK maintenance.Contract-first: O arquivo.protoé o contrato. Stubs de cliente são gerados automaticamente — sem manutenção manual de SDK. - Deadlines: Every gRPC call carries a deadline. Clients and servers both honor it. This prevents runaway requests from consuming resources indefinitely.Deadlines: Cada chamada gRPC carrega um deadline. Clientes e servidores o respeitam. Isso evita que requisições descontroladas consumam recursos indefinidamente.
// order_service.proto — contract-first API definition
syntax = "proto3";
package orders;
service OrderService {
rpc PlaceOrder (PlaceOrderRequest) returns (PlaceOrderResponse);
rpc StreamOrderUpdates (OrderId) returns (stream OrderEvent);
}
message PlaceOrderRequest {
string customer_id = 1;
repeated OrderItem items = 2;
string payment_method = 3;
}
message PlaceOrderResponse {
string order_id = 1;
string status = 2;
int64 estimated_delivery_unix = 3;
}Pattern: Use REST for your public API Gateway layer. Use gRPC for internal service-to-service calls. The protocol boundary at the gateway gives you the best of both: broad client compatibility externally and performance + type safety internally.Padrão: Use REST para sua camada de API Gateway pública. Use gRPC para chamadas internas serviço-a-serviço. A fronteira de protocolo no gateway oferece o melhor dos dois mundos: ampla compatibilidade de clientes externamente e performance + segurança de tipos internamente.
API GatewayAPI Gateway
Clients should not call individual microservices directly. An API Gateway is the single entry point for all external traffic. It handles cross-cutting concerns that would otherwise be duplicated across every service:Clientes não devem chamar microsserviços individuais diretamente. Um API Gateway é o ponto de entrada único para todo tráfego externo. Ele lida com concerns transversais que de outra forma seriam duplicados em cada serviço:
- Authentication & Authorization: Validate JWT tokens or API keys once, at the edge. Services downstream trust the gateway.Autenticação e Autorização: Valide tokens JWT ou API keys uma vez, na borda. Os serviços downstream confiam no gateway.
- Rate Limiting: Protect services from abusive clients without each service implementing its own throttling.Limitação de Taxa: Proteja os serviços de clientes abusivos sem que cada serviço implemente seu próprio throttling.
- Request routing: Route
/orders/**to Order Service,/catalog/**to Catalog Service.Roteamento de requisições: Roteie/orders/**para o Order Service,/catalog/**para o Catalog Service. - Protocol translation: Accept REST externally, translate to gRPC internally.Tradução de protocolo: Aceite REST externamente, traduza para gRPC internamente.
- Response aggregation (BFF pattern): Aggregate responses from multiple services into a single payload optimized for the client.Agregação de respostas (padrão BFF): Agregue respostas de múltiplos serviços em um único payload otimizado para o cliente.
auth · rate-limit · routing
Service MeshService Mesh
A Service Mesh (Istio, Linkerd) takes the cross-cutting concerns one step further — to the infrastructure layer. A sidecar proxy (Envoy) is injected alongside every service container and intercepts all inbound/outbound traffic transparently.Um Service Mesh (Istio, Linkerd) leva os concerns transversais um passo adiante — para a camada de infraestrutura. Um proxy sidecar (Envoy) é injetado ao lado de cada container de serviço e intercepta todo o tráfego de entrada/saída de forma transparente.
This gives you, without any application code changes:Isso oferece, sem nenhuma mudança no código da aplicação:
- mTLS everywhere: All service-to-service traffic is encrypted and mutually authenticated automatically.mTLS em todo lugar: Todo tráfego serviço-a-serviço é criptografado e mutuamente autenticado automaticamente.
- Traffic management: Canary releases, traffic splitting, and fault injection through configuration — not code.Gerenciamento de tráfego: Canary releases, divisão de tráfego e injeção de falhas por configuração — não código.
- Observability: Distributed traces, per-route latency histograms, and error rates — automatically collected at the proxy layer.Observabilidade: Traces distribuídos, histogramas de latência por rota e taxas de erro — coletados automaticamente na camada do proxy.
- Retry and timeout policies: Defined as mesh policy, applied consistently across all services.Políticas de retry e timeout: Definidas como política do mesh, aplicadas consistentemente em todos os serviços.
Caveat: A service mesh adds significant operational complexity. It is justified when you have 10+ services with complex traffic management needs. For smaller systems, handle retries and timeouts in application code or at the gateway — do not over-engineer.Cuidado: Um service mesh adiciona complexidade operacional significativa. É justificado quando você tem 10+ serviços com necessidades complexas de gerenciamento de tráfego. Para sistemas menores, trate retries e timeouts no código da aplicação ou no gateway — não sobrengenhe.
The Saga Pattern: Distributed TransactionsO Padrao Saga: Transacoes Distribuidas
In a monolith, a database transaction guarantees ACID properties across multiple operations. In microservices, each service owns its data. There is no global transaction manager. When a business operation spans multiple services — place order, reserve inventory, charge payment, send confirmation — how do you ensure consistency?Em um monolito, uma transação de banco de dados garante propriedades ACID em múltiplas operações. Em microsserviços, cada serviço possui seus dados. Não há gerenciador de transações global. Quando uma operação de negócio abrange múltiplos serviços — fazer pedido, reservar estoque, cobrar pagamento, enviar confirmação — como garantir consistência?
The Saga pattern replaces a distributed transaction with a sequence of local transactions, each publishing an event or command that triggers the next step. If a step fails, compensating transactions roll back the preceding steps.O padrão Saga substitui uma transação distribuída por uma sequência de transações locais, cada uma publicando um evento ou comando que aciona o próximo passo. Se uma etapa falha, transações compensatórias desfazem as etapas anteriores.
Choreography-based SagaSaga Baseada em Coreografia
Each service listens for events and decides what to do next. There is no central coordinator.Cada serviço escuta eventos e decide o que fazer a seguir. Não há coordenador central.
# Choreography Saga — order placement flow
# 1. Order Service
OrderPlaced event → published to Kafka
# 2. Inventory Service listens
on OrderPlaced:
if stock_available:
reserve_items()
publish InventoryReserved
else:
publish InventoryReservationFailed
# 3. Payment Service listens
on InventoryReserved:
if charge_succeeds:
publish PaymentCharged
else:
publish PaymentFailed ← triggers compensation
# 4. Order Service listens
on PaymentFailed:
update_order_status(CANCELLED) ← compensating transaction
publish OrderCancelled
# 5. Inventory Service listens
on OrderCancelled:
release_reserved_items() ← compensating transactionOrchestration-based SagaSaga Baseada em Orquestracao
A central Saga Orchestrator coordinates the flow by sending commands to each service and handling the responses. The business logic lives in one place, making it easier to understand and debug.Um Orquestrador de Saga central coordena o fluxo enviando comandos para cada serviço e tratando as respostas. A lógica de negócio fica em um lugar, tornando mais fácil entender e depurar.
// Orchestrator (Java/Spring State Machine)
@SagaOrchestrator
public class PlaceOrderSaga {
@StartSaga
@SagaEventHandler(associationProperty = "orderId")
public void on(OrderCreatedEvent event) {
commandGateway.send(new ReserveInventoryCommand(event.getOrderId(), event.getItems()));
}
@SagaEventHandler(associationProperty = "orderId")
public void on(InventoryReservedEvent event) {
commandGateway.send(new ChargePaymentCommand(event.getOrderId(), event.getAmount()));
}
@SagaEventHandler(associationProperty = "orderId")
public void on(PaymentFailedEvent event) {
// Compensate: release inventory
commandGateway.send(new ReleaseInventoryCommand(event.getOrderId()));
commandGateway.send(new CancelOrderCommand(event.getOrderId()));
}
@EndSaga
@SagaEventHandler(associationProperty = "orderId")
public void on(PaymentChargedEvent event) {
commandGateway.send(new ConfirmOrderCommand(event.getOrderId()));
}
}Choreography
- No single point of failureSem ponto único de falha
- Services are fully decoupledServiços são totalmente desacoplados
- Hard to track overall flowDifícil rastrear o fluxo geral
- Cyclic dependencies riskRisco de dependências cíclicas
- Best for simple, short sagasMelhor para sagas simples e curtas
Orchestration
- Business logic in one placeLógica de negócio em um lugar
- Easier to test and debugMais fácil de testar e depurar
- Central point of failurePonto central de falha
- Orchestrator can become a god objectO orquestrador pode se tornar um god object
- Best for complex, multi-step flowsMelhor para fluxos complexos e de múltiplas etapas
Service DiscoveryService Discovery
In a dynamic environment where service instances scale up and down, hardcoding IP addresses is not viable. Service discovery solves this in two ways:Em um ambiente dinâmico onde instâncias de serviços sobem e descem, hardcodar endereços IP não é viável. O service discovery resolve isso de duas formas:
- Client-side discovery: The client queries a service registry (Consul, Eureka) and picks an instance. The client owns the load-balancing logic (Ribbon, Spring Cloud LoadBalancer).Service discovery no lado do cliente: O cliente consulta um registro de serviços (Consul, Eureka) e escolhe uma instância. O cliente possui a lógica de balanceamento de carga (Ribbon, Spring Cloud LoadBalancer).
- Server-side discovery: The client calls a load balancer (AWS ALB, Kubernetes Service). The load balancer queries the registry and routes. Simpler from the client's perspective — the default in Kubernetes-native architectures.Service discovery no lado do servidor: O cliente chama um load balancer (AWS ALB, Kubernetes Service). O load balancer consulta o registro e roteia. Mais simples do ponto de vista do cliente — o padrão em arquiteturas Kubernetes-nativas.
Key TakeawaysPontos Principais
- Choose synchronous communication when the caller needs the result immediately; async when it just needs to notify.Escolha comunicação síncrona quando o chamador precisa do resultado imediatamente; assíncrona quando só precisa notificar.
- Use REST for external APIs; use gRPC for internal service calls where performance and type safety matter.Use REST para APIs externas; use gRPC para chamadas internas entre serviços onde performance e segurança de tipos importam.
- Put an API Gateway in front of all external traffic — never expose individual services directly.Coloque um API Gateway na frente de todo tráfego externo — nunca exponha serviços individuais diretamente.
- Introduce a Service Mesh only when the operational complexity is justified by traffic management needs at scale.Introduza um Service Mesh apenas quando a complexidade operacional for justificada pelas necessidades de gerenciamento de tráfego em escala.
- Use the Saga pattern for distributed transactions: choreography for simple flows, orchestration for complex ones.Use o padrão Saga para transações distribuídas: coreografia para fluxos simples, orquestração para os complexos.
- Always design compensating transactions — in a distributed system, partial failures are a guarantee, not an edge case.Sempre projete transações compensatórias — em um sistema distribuído, falhas parciais são uma certeza, não um caso especial.