Capítulo 1: Introdução aos Microservices
🎯 Objetivos do Capítulo
- Compreender a diferença entre monolito e microservices
- Identificar as vantagens e desvantagens de cada abordagem
- Descobrir os padrões essenciais dos microservices
- Dominar os princípios da 12-Factor App
- Saber quando escolher qual arquitetura
1. Arquitetura Monolítica
1.1 Definição
Um monolito é uma aplicação onde todas as funcionalidades são implantadas juntas em um único processo.
1.2 Exemplo: E-Commerce Monolítico
// Structure typique d'un monolithe
src/main/java/com/ecommerce/
├── EcommerceApplication.java
├── config/
│ └── SecurityConfig.java
├── controller/
│ ├── UserController.java
│ ├── ProductController.java
│ ├── OrderController.java
│ ├── PaymentController.java
│ └── InventoryController.java
├── service/
│ ├── UserService.java
│ ├── ProductService.java
│ ├── OrderService.java
│ ├── PaymentService.java
│ └── InventoryService.java
├── repository/
│ ├── UserRepository.java
│ ├── ProductRepository.java
│ ├── OrderRepository.java
│ └── ...
└── model/
├── User.java
├── Product.java
├── Order.java
└── ...
1.3 Vantagens do Monolito
| Vantagem | Explicação |
|---|---|
| Simplicidade de desenvolvimento | Um único projeto, uma única IDE |
| Deploy simples | Um único JAR/WAR para implantar |
| Testes fáceis | Testes de integração simples |
| Transações ACID | Um único BD = transações fáceis |
| Debugging simples | Stack trace completo |
| Performance local | Sem latência de rede entre módulos |
| Refatoração fácil | IDE pode renomear em todo o projeto |
1.4 Desvantagens do Monolito
| Desvantagem | Explicação |
|---|---|
| Scaling tudo ou nada | Impossível escalar apenas um módulo |
| Deploy arriscado | Um bug = toda a aplicação fora do ar |
| Stack única | Tudo em Java (ou outro) |
| Tempo de build longo | Recompilar tudo a cada vez |
| Conflitos de equipe | Merges difíceis em projeto grande |
| Dívida técnica | Difícil de modernizar |
| Onboarding demorado | Novo dev precisa entender tudo |
1.5 Quando o Monolito se Torna Problemático
Ano 1: Startup
- 3 desenvolvedores
- 50K linhas de código
- Deploy: 5 minutos
→ Monolito PERFEITO ✅
Ano 3: Crescimento
- 30 desenvolvedores
- 500K linhas de código
- Deploy: 45 minutos
- Conflitos de Git diários
→ Monolito começa a travar ⚠️
Ano 5: Escala
- 100 desenvolvedores
- 2M linhas de código
- Deploy: 2 horas
- Black Friday: tudo cai
→ Monolito = "Big Ball of Mud" ❌
2. Arquitetura Microservices
2.1 Definição
Os microservices são uma abordagem onde a aplicação é dividida em pequenos serviços autônomos, cada um:
- Implantável de forma independente
- Com seu próprio banco de dados
- Comunicando via API (HTTP/REST ou messaging)
- Gerenciado por uma pequena equipe
2.2 Características-Chave
1. Single Responsibility
Cada serviço faz UMA coisa bem:
- user-service: gestão de usuários SOMENTE
- order-service: gestão de pedidos SOMENTE
- inventory-service: gestão de estoque SOMENTE
2. Autonomia
Cada serviço:
- Tem seu próprio repo Git
- Tem seu próprio BD (Database per Service)
- Tem seu próprio pipeline CI/CD
- Pode ser implantado sozinho
- Pode usar uma tecnologia diferente
3. Comunicação
Síncrona (HTTP/REST):
- Cliente espera a resposta
- Simples mas acoplado
Assíncrona (Messaging):
- Fire and forget
- Desacoplado mas complexo
2.3 Vantagens dos Microservices
| Vantagem | Explicação |
|---|---|
| Scaling direcionado | Escalar apenas o serviço que precisa |
| Isolamento de falhas | Um serviço fora ≠ tudo fora |
| Deploy independente | Implantar 10x por dia por serviço |
| Polyglot | Java, Node, Python, Go... conforme necessário |
| Equipes autônomas | "You build it, you run it" |
| Evolutividade | Adicionar um serviço sem mexer nos outros |
| Modernização | Reescrever um serviço por vez |
2.4 Desvantagens dos Microservices
| Desvantagem | Explicação |
|---|---|
| Complexidade distribuída | Debugging difícil |
| Latência de rede | Chamadas HTTP entre serviços |
| Transações distribuídas | ACID impossível, Sagas necessárias |
| Consistência eventual | Dados não sincronizados imediatamente |
| Operações complexas | Monitoring, logging, tracing |
| Custo de infraestrutura | Mais servidores, mais BDs |
| Overhead | Cada serviço = overhead de memória |
3. Comparação Detalhada
3.1 Tabela Comparativa
| Critério | Monolito | Microservices |
|---|---|---|
| Complexidade inicial | Baixa | Alta |
| Complexidade a longo prazo | Alta | Controlada |
| Deploy | Tudo ou nada | Independente |
| Scaling | Vertical | Horizontal |
| Tecnologia | Única | Polyglot |
| Equipe | Uma equipe | Equipes por serviço |
| Banco de dados | Compartilhado | Por serviço |
| Transações | ACID simples | Sagas (complexo) |
| Debugging | Simples | Distribuído (complexo) |
| Latência | Baixa | Mais alta |
| Custo inicial | Baixo | Alto |
| Time to market | Rápido no início | Mais lento no início |
3.2 Critérios de Decisão
4. Padrões Essenciais de Microservices
4.1 API Gateway Pattern
Responsabilidades:
- Roteamento das requisições
- Autenticação/Autorização
- Rate Limiting
- Load Balancing
- SSL Termination
- Caching
- Transformação de Request/Response
4.2 Service Discovery Pattern
Como funciona:
- Serviço inicia → registra no Eureka
- Cliente quer chamar o Service A
- Cliente pergunta ao Eureka: "Onde está o Service A?"
- Eureka retorna a lista de instâncias
- Cliente escolhe uma instância (load balancing client-side)
- Cliente chama diretamente a instância
4.3 Circuit Breaker Pattern
Estados:
- CLOSED: Normal, as chamadas passam
- OPEN: Muitos erros, chamadas bloqueadas → fallback
- HALF-OPEN: Testa se o serviço voltou
// Exemple avec Resilience4j
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
public User getUser(Long id) {
return userServiceClient.getUser(id); // Appel distant
}
public User getUserFallback(Long id, Exception ex) {
return new User(id, "Unknown", "Service temporarily unavailable");
}
4.4 Saga Pattern (Transações Distribuídas)
Em um monolito:
@Transactional
public void createOrder(Order order) {
orderRepository.save(order); // ✅
inventoryService.reserve(order); // ✅
paymentService.charge(order); // ❌ Falha → ROLLBACK tudo
}
Com microservices (Saga Choreography):
4.5 Event Sourcing & CQRS
Eventos ao invés de estados:
// Ao invés de: UPDATE orders SET status = 'SHIPPED'
// Armazenamos eventos:
OrderCreated { orderId: 1, items: [...], timestamp: ... }
PaymentReceived { orderId: 1, amount: 100, timestamp: ... }
OrderShipped { orderId: 1, trackingNumber: "ABC123", timestamp: ... }
5. The Twelve-Factor App
Os 12 fatores são princípios para construir aplicações cloud-native:
Os 12 Fatores
| # | Fator | Descrição | Aplicação Spring Boot |
|---|---|---|---|
| 1 | Codebase | Um repo = uma app | Um repo Git por serviço |
| 2 | Dependencies | Declarar explicitamente | pom.xml / build.gradle |
| 3 | Config | Configuração no ambiente | application.yml + env vars |
| 4 | Backing Services | Tratar como recursos anexados | DataSource configurável |
| 5 | Build, Release, Run | Separar etapas | Pipeline CI/CD |
| 6 | Processes | Stateless | Sessão no Redis |
| 7 | Port Binding | Exportar via porta | Tomcat embarcado |
| 8 | Concurrency | Escalar via processos | Múltiplas instâncias |
| 9 | Disposability | Inicialização/parada rápida | Graceful shutdown |
| 10 | Dev/Prod Parity | Ambientes similares | Docker |
| 11 | Logs | Tratar como stream | stdout → coletor |
| 12 | Admin Processes | Tarefas admin one-off | Spring Batch |
Exemplo: Fator 3 - Configuração
# ❌ Ruim: Configuração hardcoded
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret123
# ✅ Bom: Configuração externalizada
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
Exemplo: Fator 6 - Processes Stateless
// ❌ Ruim: Estado em memória
@RestController
public class CartController {
private Map<Long, Cart> carts = new HashMap<>(); // Estado local!
@PostMapping("/cart/{userId}/add")
public void addItem(@PathVariable Long userId, @RequestBody Item item) {
carts.computeIfAbsent(userId, Cart::new).add(item);
}
}
// ✅ Bom: Estado externalizado (Redis)
@RestController
public class CartController {
private final RedisTemplate<String, Cart> redis;
@PostMapping("/cart/{userId}/add")
public void addItem(@PathVariable Long userId, @RequestBody Item item) {
String key = "cart:" + userId;
Cart cart = redis.opsForValue().get(key);
if (cart == null) cart = new Cart();
cart.add(item);
redis.opsForValue().set(key, cart);
}
}
6. Domain-Driven Design (DDD) e Microservices
6.1 Bounded Context
O Bounded Context define as fronteiras de um microservice:
6.2 Mesmo Conceito, Contextos Diferentes
// No Sales Context
public class Customer {
private Long id;
private String name;
private String email;
private List<Order> orders;
private LoyaltyLevel loyaltyLevel;
}
// No Shipping Context
public class Customer {
private Long id;
private String name;
private Address shippingAddress;
private String phoneNumber;
}
// No Billing Context
public class Customer {
private Long id;
private String name;
private PaymentMethod defaultPaymentMethod;
private BillingAddress billingAddress;
}
7. Anti-Patterns a Evitar
7.1 Distributed Monolith
❌ ANTI-PATTERN: Monolito distribuído
- 10 serviços que DEVEM ser implantados juntos
- Esquema de BD compartilhado
- Forte acoplamento entre serviços
- O pior dos dois mundos!
7.2 Nano-Services
❌ ANTI-PATTERN: Serviços muito pequenos
- Um serviço por entidade
- UserService, UserAddressService, UserPreferenceService
- Overhead enorme
- Transações impossíveis
7.3 Shared Database
❌ ANTI-PATTERN: Banco de dados compartilhado
Service A ──┬──> PostgreSQL <──┬── Service B
│ │
└──> orders table <┘
Problema:
- Forte acoplamento via esquema
- Impossível alterar uma tabela
- Tentação de transações cruzadas
7.4 Sync Everywhere
❌ ANTI-PATTERN: Tudo síncrono
Order Service
↓ HTTP sync
Inventory Service
↓ HTTP sync
Payment Service
↓ HTTP sync
Notification Service
Problema:
- Latência acumulada
- Um serviço lento = tudo lento
- Cascade failures
8. Migração Monolito → Microservices
8.1 Estratégia: Strangler Fig Pattern
8.2 Etapas da Migração
- Identificar os Bounded Contexts no monolito
- Começar pelo menos arriscado (ex: notificações)
- Extrair para um novo serviço
- Roteamento via API Gateway
- Remover o código antigo do monolito
- Repetir para o próximo contexto
9. Resumo
Pontos-Chave
✅ Monolito = Simples mas não escala
✅ Microservices = Complexo mas flexível
✅ Comece monolito se possível
✅ Migre quando o monolito travar
✅ 12-Factor App = boas práticas cloud-native
✅ DDD = ajuda a definir as fronteiras
✅ Padrões = Circuit Breaker, Saga, Event Sourcing
Checklist Antes de Microservices
- Equipe experiente em DevOps?
- Infraestrutura Kubernetes/Docker?
- Monitoring e logging centralizados?
- CI/CD maduro?
- Bounded Contexts bem definidos?
- Problemas reais de scaling?
10. Quiz de Validação
Pergunta 1
Qual padrão evita a cascata de falhas entre serviços?
Ver resposta
Circuit Breaker - Ele detecta falhas e "abre" o circuito para retornar um fallback imediatamente ao invés de esperar timeouts.
Pergunta 2
Por que "Database per Service" é importante?
Ver resposta
- Desacoplamento: Cada serviço pode evoluir seu esquema de forma independente
- Autonomia: Sem dependência de um DBA central
- Tecnologia: PostgreSQL para um serviço, MongoDB para outro
- Evita acoplamento oculto via esquema compartilhado
Pergunta 3
O que é um "Distributed Monolith"?
Ver resposta
Um anti-pattern onde existem vários serviços mas:
- Precisam ser implantados juntos
- Compartilham um banco de dados
- São fortemente acoplados
Você tem a complexidade dos microservices SEM as vantagens.