Visão geral
Imagine a loja como uma única aplicação Spring Boot: login, catálogo, pedidos e pagamentos em um só código-fonte, um só banco de dados, um só .jar para implantar. Na Black Friday, apenas o checkout fica sobrecarregado, mas a única forma de escalá-lo é rodar mais cópias de tudo. Um bug na página do catálogo derruba o login junto. Três times agora compartilham o código, e cada release exige que todos concordem com o mesmo dia. Não há nada de errado com o código — o formato do sistema é que não se encaixa mais na organização.
O estilo de microsserviços é uma resposta a esse problema específico: à medida que times e bases de código crescem, um único monólito fica cada vez mais caro de mudar — modificar um módulo exige entender e testar o sistema inteiro, e um deploy exige coordenação com a organização toda.
Microsserviços é um estilo arquitetural que estrutura uma aplicação como uma coleção de serviços pequenos e autônomos, cada um modelado em torno de um domínio de negócio específico52. Em vez de construir uma única grande unidade implantável (um monólito), o sistema é composto de partes implantáveis de forma independente, que colaboram por meio de APIs bem definidas.
Objetivos de aprendizagem
Ao final desta aula, você deve ser capaz de:
- Comparar um monólito, um monólito modular e microsserviços, e justificar quando a divisão compensa o seu custo.
- Explicar a Lei de Conway e os princípios centrais de microsserviços (capacidade única, independência, isolamento de dados).
- Escolher entre comunicação síncrona e assíncrona para uma interação, e estimar o custo de disponibilidade de uma cadeia síncrona.
- Aplicar o Domain-Driven Design (bounded contexts, agregados, mapas de contexto) para encontrar as fronteiras dos serviços.
- Descrever os componentes de infraestrutura de uma plataforma de microsserviços e acompanhar uma requisição pela arquitetura do curso.
Roteiro
- Esta página — por que e quando dividir, Lei de Conway, princípios, posse dos dados, comunicação síncrona vs. assíncrona, os serviços da loja.
- Domain-Driven Design — o vocabulário e as técnicas para encontrar boas fronteiras entre serviços.
- Arquitetura e componentes — balanceador de carga, gateway, provedor de identidade, descoberta, configuração, broker, observabilidade e a arquitetura de referência do curso.
Slides
Clique nos slides e use ← → para navegar; F abre em tela cheia e P abre a visão do apresentador, com as notas. Abrir os slides em uma nova aba
Monólito vs. microsserviços
flowchart LR
subgraph mono [Monólito]
direction TB
ui_m[UI]
biz_m[Lógica de negócio\nPedidos · Auth · Pagamentos · Estoque]
data_m[Banco de dados único]
ui_m --> biz_m --> data_m
end
subgraph micro [Microsserviços]
direction TB
gw[API Gateway]
s1[Serviço de\nPedidos]
s2[Serviço de\nAuth]
s3[Serviço de\nEstoque]
db1[(BD Pedidos)]
db2[(BD Auth)]
db3[(BD Estoque)]
gw --> s1 & s2 & s3
s1 --> db1
s2 --> db2
s3 --> db3
end Nenhum dos dois estilos é universalmente melhor. A escolha certa depende do tamanho do time, da complexidade do domínio e da maturidade operacional:
| Dimensão | Monólito | Microsserviços |
|---|---|---|
| Deploy | Uma unidade — simples de entregar | Independente por serviço — complexo de coordenar |
| Escalabilidade | Escala a aplicação inteira | Escala apenas o serviço gargalo |
| Velocidade de desenvolvimento | Rápida no início | Setup mais lento; mais rápida em escala |
| Complexidade operacional | Baixa | Alta — exige orquestração, rastreamento, descoberta de serviços |
| Consistência dos dados | Transações ACID | Consistência eventual entre serviços |
| Perfil de time | Time pequeno, no mesmo lugar | Vários times, cada um dono de um serviço |
Dois termos da tabela merecem uma definição. Uma transação ACID (Atomicidade, Consistência, Isolamento, Durabilidade) permite que o monólito altere pedidos e estoque em uma única operação de banco de dados, que acontece por inteiro ou não acontece. Entre serviços não existe transação compartilhada: cada serviço grava os seus próprios dados, e o sistema fica consistente um instante depois — consistência eventual (veja Teorema CAP).
Comece com um monólito
A maioria dos sistemas de microsserviços bem-sucedidos começou como monólito. Identifique fronteiras de domínio estáveis (usando Domain-Driven Design) antes de dividir. A decomposição prematura cria um monólito distribuído — toda a complexidade dos microsserviços, sem nenhum dos benefícios de independência.
O meio-termo: um monólito modular. Uma única unidade implantável, mas dividida internamente em módulos com interfaces explícitas e sem tabelas compartilhadas — cada módulo poderia virar um serviço depois. Isso dá ao time prática com fronteiras antes de pagar o preço da rede, e muitas vezes é a resposta certa para um time pequeno.
Sintomas de um monólito distribuído
- Os serviços precisam ser implantados juntos ("release train"), porque uma mudança em um quebra os outros.
- Dois serviços leem e escrevem nas mesmas tabelas.
- Uma requisição do usuário dispara uma longa cadeia de chamadas síncronas; se qualquer elo cair, tudo cai.
- Uma biblioteca compartilhada com classes de domínio obriga todos os serviços a atualizar ao mesmo tempo.
Lei de Conway
"Qualquer organização que projeta um sistema produzirá um projeto cuja estrutura é uma cópia da estrutura de comunicação da organização." — Melvin Conway, 19676
As fronteiras entre serviços e as fronteiras entre times convergem ao longo do tempo. A simulação abaixo mostra como um código inicialmente unificado se fragmenta em serviços que espelham os padrões de comunicação dos times que o construíram:
A lei também funciona ao contrário, e é assim que as organizações a usam de propósito: a manobra inversa de Conway (inverse Conway maneuver) organiza os times primeiro (um time por capacidade de negócio) para que a arquitetura venha em seguida.
Princípios fundamentais
| Princípio | Significado |
|---|---|
| Responsabilidade única | Cada serviço implementa exatamente uma capacidade de negócio |
| Independência | Os serviços podem ser desenvolvidos, implantados e escalados sem coordenação com os outros |
| Isolamento de dados | Cada serviço é dono do próprio banco de dados — sem esquema compartilhado, sem tabelas compartilhadas |
| Governança descentralizada | Os times escolhem a tecnologia que melhor se adapta ao seu serviço |
| Isolamento de falhas | Um serviço com falha não se propaga em cascata nem derruba o sistema inteiro |
| Comunicação API-first | Os serviços interagem por meio de contratos explícitos e versionados |
| Observabilidade | Monitoramento abrangente, rastreamento distribuído e logs estruturados são pré-requisitos |
Quão pequeno é "micro"? Não é uma quantidade de linhas. Um serviço tem o tamanho certo quando um time consegue ser dono dele de ponta a ponta, ele pode ser implantado sem coordenação com os outros e corresponde a uma capacidade de negócio. Serviços pequenos demais ("nanosserviços", por exemplo, um serviço por tabela) passam mais tempo conversando entre si do que trabalhando.
A loja, decomposta
O curso constrói uma plataforma passo a passo. Cada serviço é dono de uma capacidade e dos seus próprios dados4:
| Serviço | Capacidade | Dono de | Conversa com | Construído em |
|---|---|---|---|---|
gateway-service | Ponto de entrada único, roteamento, verificação de autenticação | — | todos os serviços | Hands-on 2, 3 |
account-service | Contas de usuário | esquema accounts | — | Hands-on 1 |
auth-service | Cadastro, login, emissão e resolução de JWTs | — (usa account) | account | Hands-on 3 |
product-service | Catálogo de produtos | dados de produtos | — | Product API |
order-service | Pedidos do usuário atual | dados de pedidos | product, exchange, Kafka | Order API |
exchange-service (Python) | Conversão de moedas | — | API de cotações de terceiros | Exchange API |
notification-service (Python) | E-mails de confirmação de pedido | — | Kafka | Hands-on 9 |
Observe a governança descentralizada em ação: os serviços de câmbio e de notificação são escritos em Python, os demais em Java — o contrato é HTTP/JSON ou um evento Kafka, então a linguagem não vaza.
Ser dono dos dados
Banco de dados por serviço (database-per-service) significa que nenhum serviço lê ou escreve nas tabelas de outro serviço. Isso não exige um servidor de banco de dados por serviço. No hands-on, um único container Postgres hospeda vários esquemas, e cada serviço é o único que mexe no seu (o serviço de contas define hibernate.default_schema: accounts e roda as próprias migrações Flyway):
flowchart LR
subgraph pg ["um servidor Postgres"]
s1[("esquema accounts")]
s2[("esquema products")]
s3[("esquema orders")]
end
account["account-service"] --> s1
product["product-service"] --> s2
order["order-service"] --> s3
order -->|"GET /products/{id}"| product
order -.-x|"sem SQL direto"| s2 Quando o serviço de pedidos precisa do preço de um produto, ele pergunta à API de produtos, nunca à tabela de produtos. Isso deixa o time de produtos livre para mudar o seu esquema. Disso decorrem duas consequências:
- Consultas entre serviços viram chamadas de API (ou uma cópia local dos dados, mantida atualizada por eventos — veja CQRS).
- Transações de negócio entre serviços não podem usar uma única transação ACID. Elas viram uma sequência de transações locais coordenadas por eventos, com ações de compensação quando um passo falha — o padrão saga4.
Comunicação entre serviços
Os serviços de um sistema de microsserviços se comunicam de duas formas fundamentalmente diferentes, cada uma com trade-offs próprios:
Quem chama envia uma requisição e espera a resposta. É simples de entender, mas cria acoplamento temporal — quem chama não consegue concluir a operação se o serviço chamado estiver fora do ar.
sequenceDiagram
participant Client as Cliente
participant Gateway
participant Order Service as Serviço de Pedidos
participant Inventory Service as Serviço de Estoque
Client->>+Gateway: POST /orders
Gateway->>+Order Service: cria o pedido
Order Service->>+Inventory Service: reserva os itens
Inventory Service-->>-Order Service: reservado ✓
Order Service-->>-Gateway: 201 Created
Gateway-->>-Client: 201 Created Use quando: quem chama precisa do resultado imediatamente (por exemplo, a resposta do checkout, a validação de autenticação).
O produtor publica um evento e segue em frente. Os consumidores processam o evento de forma independente, no seu próprio tempo. Isso elimina o acoplamento temporal — os serviços podem estar fora do ar e processar a mensagem quando se recuperarem.
sequenceDiagram
participant Order Service as Serviço de Pedidos
participant Message Broker as Message broker
participant Inventory Service as Serviço de Estoque
participant Notification Service as Serviço de Notificação
Order Service->>Message Broker: evento OrderPlaced
Note over Order Service: segue imediatamente
Message Broker->>Inventory Service: OrderPlaced
Message Broker->>Notification Service: OrderPlaced
Inventory Service->>Message Broker: InventoryReserved Use quando: a consistência estrita não é necessária de imediato (por exemplo, enviar um e-mail, atualizar analytics, reservar estoque).
Na loja, o serviço de pedidos chama o serviço de produtos de forma síncrona (ele precisa do preço para calcular o total antes de responder) e publica um evento OrderCreated no Kafka de forma assíncrona, para que o serviço de notificação envie o e-mail sem deixar o cliente esperando (Hands-on 9).
Cadeias síncronas multiplicam as falhas
Se uma requisição precisa passar por três serviços, e cada um fica disponível 99,9% do tempo, a cadeia fica disponível apenas 0,999 × 0,999 × 0,999 ≈ 99,7% do tempo — de cerca de 8,8 horas de indisponibilidade por ano para cerca de 26 horas. Cada salto síncrono a mais reduz a disponibilidade e adiciona latência. Mantenha as cadeias curtas, use timeouts e circuit breakers (Padrões de resiliência) e mova para eventos o trabalho que não precisa de resposta imediata (Arquitetura orientada a eventos).
Tópicos abordados
-
Domain-Driven Design
O vocabulário e as técnicas para identificar as fronteiras dos serviços: linguagem ubíqua, bounded contexts, entidades, objetos de valor, agregados, eventos de domínio, repositórios e mapas de contexto.
-
Arquitetura e componentes
Os blocos de construção de um sistema de microsserviços em produção: balanceadores de carga, API gateway, provedor de identidade, descoberta de serviços, serviço de configuração, message broker, observabilidade e a arquitetura de referência do curso1.
Pontos-chave
- Microsserviços trocam complexidade operacional por deploy independente, escalabilidade e autonomia dos times — só vale a pena quando esses são problemas reais.
- Comece com um monólito (modular); divida ao longo de fronteiras de domínio estáveis, encontradas com DDD3, ou você terá um monólito distribuído.
- Lei de Conway: as fronteiras dos serviços espelham a comunicação entre os times — projete os dois juntos.
- Cada serviço é dono de uma capacidade e dos seus dados; os outros usam a sua API, nunca as suas tabelas.
- Chamadas síncronas são simples, mas acoplam a disponibilidade; eventos desacoplam produtores de consumidores.
Conexões
- Por que a rede torna isso difícil: Sistemas distribuídos.
- Os contratos entre serviços: API.
- Construa a plataforma: Hands-on 1 — CRUD, Hands-on 2 — Gateway, Hands-on 3 — Segurança.
- Dentro de um serviço: Arquiteturas de software (clean e hexagonal).
- Integração assíncrona: Arquitetura orientada a eventos e Hands-on 9 — Kafka.
-
XU, A. System Design 101 ↩
-
NEWMAN, S. Building Microservices, 2nd ed. O'Reilly, 2021. ↩
-
EVANS, E. Domain-Driven Design. Addison-Wesley, 2003. ↩
-
RICHARDSON, C. Microservices Patterns. ↩↩
-
LEWIS, J.; FOWLER, M. Microservices — a definition of this new architectural term. martinfowler.com, 2014. ↩
-
CONWAY, M. E. How Do Committees Invent? Datamation, 14(4), 1968, p. 28–31. ↩