AWS na Prática · 01 — Microsserviços

O estilo arquitetural por trás de sistemas distribuídos modernos.

17 min de leitura

O estilo arquitetural por trás de sistemas distribuídos modernos.

Sobre este módulo

Antes de subir qualquer coisa na AWS, você precisa entender o que são microserviços de verdade — não como buzzword, mas como decisão arquitetural com prós, contras e princípios concretos. Aqui vamos desenhar formalmente os três serviços do projeto.

Nível: Intermediário · Duração estimada: 3-4 horas


Sumário

  1. Por que microserviços existem?
  2. O que define um microserviço
  3. Princípios fundamentais
  4. Comunicação entre serviços
  5. Os trade-offs reais
  6. Desenhando o nosso sistema
  7. Contratos das APIs
  8. Estratégia de dados
  9. Exercícios de fixação
  10. Próximos passos

01. Por que microserviços existem?

Antes de aprender qualquer técnica, é importante entender o problema que ela resolve. Microserviços não são uma moda — eles surgiram como resposta concreta às limitações do modelo monolítico em sistemas que cresceram demais.

O monolito e seus limites

Em um monolito, todo o código de uma aplicação vive em um único processo, com um único banco de dados, um único deploy. Para projetos pequenos e médios, isso é ótimo: simples de desenvolver, simples de testar, simples de operar.

O problema aparece quando o sistema cresce. Times maiores começam a pisar nos pés uns dos outros, deploys ficam arriscados (tudo sobe junto), uma falha em uma parte do código pode derrubar a aplicação inteira, e escalar uma funcionalidade específica significa escalar o monolito todo — desperdício de recursos.

A proposta dos microserviços

A ideia central é simples: quebrar o monolito em serviços pequenos e independentes, cada um com responsabilidade clara, banco próprio e ciclo de deploy autônomo. A comunicação entre eles acontece via APIs ou mensageria.

Cada serviço pode ser desenvolvido por um time diferente, escalado de forma independente, escrito em linguagem diferente se fizer sentido, e — talvez o mais importante — falhar de forma isolada sem derrubar o sistema todo.

Por que estudar microserviços agora?

Independente de você usar microserviços no dia a dia, os princípios por trás deles — separação de responsabilidades, contratos bem definidos, comunicação assíncrona, idempotência — são fundamentais para qualquer sistema moderno. Aprender microserviços é aprender a pensar em sistemas distribuídos.

02. O que define um microserviço

Existe muita confusão sobre o que é e o que não é um microserviço. Ter três aplicações Node.js separadas no mesmo banco de dados não é uma arquitetura de microserviços — é um monolito distribuído, que combina o pior dos dois mundos.

As características essenciais

  • Responsabilidade única — Cada serviço cobre um único domínio de negócio. Orders cuida de pedidos, Inventory cuida de estoque. Não há sobreposição.
  • Banco de dados privado — Cada serviço é dono dos seus dados. Outros serviços não acessam o banco diretamente — só via API ou eventos.
  • Deploy independente — Subir uma nova versão do Orders não exige tocar no Inventory. Os ciclos de release são totalmente desacoplados.
  • Falha isolada — Se o Payments cair, o Orders continua aceitando pedidos (eles ficam pendentes). Um serviço fora do ar não derruba o sistema todo.
  • Stack independente — Cada serviço pode usar a linguagem, framework e banco que melhor servem ao seu domínio. (No nosso projeto vamos padronizar Node.js para simplificar, mas é uma escolha consciente.)

Tamanho do serviço: o quão “micro”?

Não existe regra fixa de quantas linhas de código ou quantos endpoints definem um microserviço. A métrica útil é o contexto de negócio (Bounded Context, do Domain-Driven Design): um serviço deve cobrir tudo que pertence ao mesmo domínio e nada que pertença a outro.

Pedidos têm regras específicas: criação, cancelamento, status, histórico. Tudo isso é Orders. Estoque tem outras regras: entradas, saídas, reservas, alertas de baixa. Tudo isso é Inventory. Misturar os dois em um único serviço seria voltar para a confusão do monolito.

03. Princípios fundamentais

Os princípios abaixo são os que mais vão guiar suas decisões ao construir o projeto. Releia esta seção sempre que estiver em dúvida sobre como dividir responsabilidades.

Single Responsibility (responsabilidade única)

Um serviço deve ter uma razão para mudar. Se você precisa alterar Orders tanto quando muda a regra de pedido quanto quando muda a regra de pagamento, as responsabilidades estão misturadas. Separe.

Database per Service

Cada serviço tem seu próprio banco. Isso parece estranho no começo (e gera duplicação de dados em alguns casos), mas é o que garante a independência real. Compartilhar banco entre serviços cria acoplamento invisível: uma alteração de schema afeta múltiplos serviços, e nunca fica claro quem é dono de cada tabela.

Smart Endpoints, Dumb Pipes

A inteligência fica nos serviços, não no canal de comunicação. Use mensageria simples (SNS/SQS, HTTP) e coloque toda a lógica de negócio dentro dos serviços. Evite ESBs complexos e fluxos que escondem a lógica em configurações de pipeline.

Decentralized Governance

Não tente padronizar tudo. Dois serviços podem usar bancos diferentes (um SQL, outro NoSQL) se isso fizer sentido para os domínios deles. A padronização excessiva é um vestígio do pensamento monolítico.

Design for Failure

Sistemas distribuídos falham. Sempre. Rede oscila, serviços ficam fora do ar, mensagens se perdem. Você precisa desenhar para falhar: timeouts, retries, circuit breakers, dead letter queues, idempotência.

No nosso projeto, vamos aplicar esses padrões na prática. Se o Payments estiver fora, o Orders ainda aceita pedidos (status = pending_payment) e o sistema se recupera quando o serviço voltar.

Idempotência

Uma operação é idempotente quando executá-la mais de uma vez tem o mesmo efeito que executá-la uma vez. Em sistemas distribuídos, isso é essencial: a mesma mensagem pode ser entregue duas vezes (por uma falha transitória), e seu serviço precisa lidar com isso sem corromper dados.

Exemplo: criar um pedido com o mesmo idempotency_key duas vezes deve criar o pedido uma única vez. A segunda chamada deve retornar o pedido já existente, não criar duplicado.

04. Comunicação entre serviços

Como os serviços conversam entre si é uma das decisões mais importantes da arquitetura. Há duas grandes categorias, com características muito diferentes.

Síncrona: chamada direta (HTTP/REST)

O serviço A chama o serviço B e espera a resposta. É o modelo mais intuitivo, parecido com chamar uma função. Funciona bem quando A precisa do dado de B antes de continuar.

  • Vantagens: simples, debug direto, semântica clara.
  • Desvantagens: acoplamento temporal (A só funciona se B estiver no ar), latência somada, propagação de falhas.

Assíncrona: mensageria (SNS/SQS, Kafka)

O serviço A publica um evento e segue sua vida. Outros serviços interessados consomem o evento quando podem. Não há espera, não há acoplamento temporal.

  • Vantagens: serviços desacoplados, resiliente a falhas, escalabilidade natural.
  • Desvantagens: debug mais complexo, eventual consistency, ordem de eventos não garantida (em geral).

Quando usar cada um?

Não é “escolher um e usar sempre”. Você usa os dois em momentos diferentes:

CenárioTipo recomendadoPor quê?
Cliente cria pedidoSíncrono (HTTP)Cliente precisa de resposta imediata.
Notificar estoqueAssíncrono (evento)Estoque não precisa ser atualizado em tempo real.
Validar usuárioSíncrono (HTTP)Decisão depende da resposta.
Enviar e-mailAssíncrono (evento)Pode ser feito em background.
Consultar saldoSíncrono (HTTP)Dado em tempo real é necessário.
Auditoria de açõesAssíncrono (evento)Não bloqueia o fluxo principal.

Eventual consistency

Quando você usa eventos, perde a consistência forte. Se um pedido é criado em Orders, vai levar alguns milissegundos (ou segundos) até o Inventory saber. Durante esse intervalo, o sistema está em um estado inconsistente — e está tudo bem, contanto que você desenhe para isso.

05. Os trade-offs reais

Microserviços não são “a evolução natural” do monolito. São uma escolha que traz novos problemas em troca de resolver outros. Conhecer esses problemas é o que separa quem usa microserviços bem de quem só está seguindo uma moda.

O que você ganha

  • Escalabilidade granular — escala só o serviço que precisa.
  • Times independentes — cada time é dono do seu serviço, ciclos de release próprios.
  • Resiliência — falhas isoladas em vez de cascata.
  • Liberdade tecnológica — escolha a stack certa para cada problema.
  • Manutenibilidade — bases de código menores são mais fáceis de entender e refatorar.

O que você perde (ou complica)

  • Complexidade operacional — agora você tem N deploys, N pipelines, N pontos de monitoramento.
  • Debugging distribuído — uma requisição passa por vários serviços; logs precisam ser correlacionados.
  • Eventual consistency — você precisa lidar com estados temporariamente inconsistentes.
  • Latência — chamadas de rede entre serviços são ordens de magnitude mais lentas que chamadas in-process.
  • Transações distribuídas — não dá para fazer ROLLBACK simples; precisa de Sagas ou compensações.
  • Custo de infraestrutura — mais serviços, mais máquinas, mais $$.

A regra dos 80/20

Em projetos reais, microserviços costumam fazer sentido em 20% dos casos que enfrentam problemas que monolitos resolvem mal: times muito grandes, necessidade de escala diferenciada por funcionalidade, ou requisitos de isolamento severo. Para os outros 80%, monolito modular é melhor.

No nosso projeto, vamos usar microserviços porque o objetivo é aprender — não porque o sistema seria grande o suficiente para justificar em produção. Importante ter clareza dessa distinção.

06. Desenhando o nosso sistema

Vamos formalizar o desenho dos três serviços do projeto. Esta é a planta que vai guiar todo o trabalho dos próximos módulos.

Identificando os domínios

Antes do código, antes da AWS, antes de qualquer coisa: quais são os domínios de negócio? No nosso e-commerce simplificado, três domínios principais emergem:

  • Orders — tudo sobre o ciclo de vida de um pedido (criação, status, cancelamento, histórico).
  • Inventory — tudo sobre estoque de produtos (disponibilidade, reservas, decrementos).
  • Payments — tudo sobre cobrança (processamento, estornos, status de pagamento).

Mapa de interações

Como esses três serviços se conectam para formar o fluxo de um pedido?

Renderizando diagrama…

Figura 1 — Mapa de interações entre os três serviços.

Por que essas escolhas?

Cliente → Orders é HTTP síncrono porque o cliente precisa do orderId imediatamente para mostrar na tela. Orders → Payments é HTTP síncrono porque o status do pedido depende do resultado do pagamento. Orders → Inventory é assíncrono via evento porque o decremento de estoque pode acontecer com alguns segundos de atraso sem prejuízo ao cliente.

07. Contratos das APIs

Em microserviços, o contrato da API é o único acordo entre os times. Mudanças de contrato quebram clientes — então essa parte exige cuidado e documentação rigorosa.

Princípios de design de API

  • Recursos como substantivos: /orders, não /getOrders.
  • Verbos HTTP semânticos: GET para ler, POST para criar, PUT/PATCH para atualizar, DELETE para remover.
  • Status codes corretos: 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 422 Unprocessable Entity, 500 Internal Server Error.
  • Versionamento explícito: /v1/orders, para permitir evoluir o contrato sem quebrar clientes existentes.
  • Idempotência: POST de criação aceita um header Idempotency-Key.
  • Paginação: nunca retorne lista sem paginar; use cursores ou offsets.

Orders Service — endpoints

MétodoCaminhoDescrição
POST/v1/ordersCria um novo pedido
GET/v1/orders/{id}Retorna um pedido pelo ID
GET/v1/ordersLista pedidos (paginado)
PATCH/v1/orders/{id}/cancelCancela um pedido
GET/v1/orders/{id}/statusConsulta status atual

Inventory Service — endpoints

MétodoCaminhoDescrição
GET/v1/products/{id}Detalhes do produto e estoque
GET/v1/productsLista de produtos (paginado)
POST/v1/products/{id}/restockRepõe estoque
GET/v1/products/{id}/availabilityVerifica disponibilidade

Payments Service — endpoints

MétodoCaminhoDescrição
POST/v1/paymentsInicia um pagamento
GET/v1/payments/{id}Consulta status de pagamento
POST/v1/payments/{id}/refundSolicita estorno

Eventos publicados (assíncronos)

Além das APIs REST, os serviços se comunicam via eventos. Cada serviço é responsável por publicar os eventos do seu domínio:

EventoPublicado porConsumido por
order.createdOrdersInventory, Notifications
order.cancelledOrdersInventory, Payments
order.paidOrdersNotifications
inventory.depletedInventoryOrders
payment.failedPaymentsOrders, Notifications

08. Estratégia de dados

Cada serviço tem o seu banco. Mas como decidir qual banco usar para cada um? E como lidar com a duplicação inevitável de dados entre serviços?

Banco por serviço, não por tabela

A regra é forte: cada serviço, seu banco. Mesmo que os três usem DynamoDB (como vamos fazer no projeto), são tabelas em contas/escopos diferentes, com permissões IAM separadas. Orders não tem acesso direto à tabela de Inventory, mesmo que a infraestrutura permita.

Duplicação de dados é normal

Em microserviços, é comum (e desejável) que dados sejam duplicados. Por exemplo: o pedido em Orders armazena productName e price no momento da compra, mesmo que esses dados também existam em Inventory. Por quê?

  • Histórico: se o preço do produto mudar amanhã, o pedido de hoje continua mostrando o preço de hoje.
  • Independência: Orders consegue mostrar detalhes do pedido mesmo se Inventory estiver fora.
  • Performance: não precisa fazer chamada de rede para outro serviço a cada listagem.

Como manter dados sincronizados?

Sincronização acontece via eventos. Quando um produto é cadastrado em Inventory, um evento product.created é publicado. Outros serviços interessados consomem esse evento e atualizam suas próprias cópias.

Isso é eventual consistency em ação: por alguns milissegundos, os dados podem estar desatualizados em Orders. Para a maioria dos casos de negócio, isso é aceitável. Para os que não são, você usa chamada síncrona.

Por que DynamoDB para o projeto?

Vamos usar DynamoDB nos três serviços por algumas razões pedagógicas e práticas:

  • Está no Free Tier permanente (25 GB de armazenamento, 25 RCUs/WCUs).
  • É serverless: não há instância para gerenciar, escala automaticamente.
  • Modelagem NoSQL te força a pensar em access patterns, o que é uma habilidade essencial.
  • Integra nativamente com Lambda (DynamoDB Streams) — vamos usar isso depois.
  • Latência de single-digit millisecond independente de escala.

09. Exercícios de fixação

Os exercícios deste módulo são em sua maioria de papel — modelagem, desenho e reflexão. Você não vai mexer na AWS ainda. Faz parte do trabalho de arquiteto: pensar antes de construir.

Exercício
01

Identifique violações de microserviço

Imagine três cenários e diga qual deles fere princípios de microserviços, e por quê: (a) dois serviços que leem da mesma tabela users; (b) um serviço de Pedidos que chama o de Pagamentos via HTTP; (c) um time precisa fazer deploy coordenado de Orders e Inventory toda vez que muda o schema de pedidos. Escreva a análise em texto.

Exercício
02

Desenhe o fluxo de cancelamento

Detalhe, em texto e com setas, o que acontece quando um cliente cancela um pedido. Quem chama quem? Quais eventos são publicados? Quais serviços consomem cada evento? Considere casos de erro: e se Payments estiver fora no momento do estorno?

Exercício
03

Adicione um quarto serviço

Suponha que precisamos adicionar um Notifications Service, responsável por enviar SMS, e-mail e push para os clientes. Modele: quais eventos ele consome? Ele tem APIs próprias? Que dados precisa duplicar de outros serviços? Onde ele se encaixa no diagrama atual?

Exercício
04

Idempotência na prática

Pense no endpoint POST /v1/orders. Se o cliente fizer a mesma chamada duas vezes (por exemplo, um retry após timeout), você não pode criar dois pedidos. Descreva a solução usando Idempotency-Key: o que o cliente envia, o que o servidor armazena, como reconhece a duplicata.

Exercício
05

Reflexão escrita

Em um texto livre (10 a 15 linhas), responda: “Por que é tentador compartilhar banco entre microserviços, e por que essa tentação está errada?” Use exemplos concretos do nosso projeto.

10. Próximos passos

Você terminou a parte conceitual mais densa do curso. A partir do próximo módulo, vamos sair do papel e começar a tocar a AWS.

O que você aprendeu

  • Por que microserviços existem e quais problemas resolvem.
  • As características essenciais que diferenciam microserviços de “monolito distribuído”.
  • Princípios fundamentais: SRP, banco por serviço, idempotência, design for failure.
  • Comunicação síncrona vs assíncrona — quando usar cada uma.
  • Os trade-offs reais de adotar essa arquitetura.
  • O desenho formal dos três serviços do projeto e seus contratos de API.
  • A estratégia de dados: duplicação controlada, sincronização via eventos.

O que vem no Módulo 02

DynamoDB e modelagem de dados. Vamos descer ao nível da estrutura concreta: como modelar as tabelas de Orders, Inventory e Payments, escolher partition keys, definir GSIs (Global Secondary Indexes) para os access patterns que precisamos, e finalmente criar as primeiras tabelas no console da AWS.

O bom design é invisível. Boa jornada — Módulo 02 a seguir.

Posts relacionados