Arquitetura é o conjunto de decisões caras de mudar

Apostila completa de Arquitetura de Software & System Design

Escrever a função certa é ofício; escolher onde ficam as fronteiras, como os serviços conversam, o que acontece quando um deles cai e como o sistema cresce 100× é arquitetura — e é isso que separa o desenvolvedor pleno do sénior. Esta apostila vai dos fundamentos (acoplamento, coesão, estilos) aos sistemas distribuídos (saga, CQRS, consistência eventual) e à entrevista de system design, com um framework replicável e exemplos resolvidos.

10 módulosMonólito modular · Microservices · EDASaga · CQRS · OutboxCache · Sharding · Circuit breakerFramework de entrevistaExercícios com gabarito
MÓDULO 01 · BÁSICO

O que é arquitetura e por que ela importa

Objetivo: definir arquitetura de forma útil, entender atributos de qualidade e trade-offs, e o que muda no papel de quem "faz arquitetura".

1.1 Uma definição que serve

Há dezenas de definições acadêmicas. A mais prática, atribuída a Ralph Johnson e popularizada por Martin Fowler: "arquitetura é o conjunto de decisões que você gostaria de ter acertado cedo — as coisas que são difíceis e caras de mudar depois". Onde ficam as fronteiras entre módulos, se há um ou vinte bancos de dados, se a comunicação é síncrona ou por eventos, qual a estratégia de consistência — mudar qualquer uma dessas em um sistema grande custa meses.

💡 Arquitetura × design

Não há uma linha nítida, mas uma régua útil: arquitetura é o que afeta a estrutura, os atributos de qualidade e é caro reverter (estilo, fronteiras, comunicação, dados). Design é o que fica dentro de um componente e é barato refatorar (padrões de classe, nome de função, estrutura de um módulo). Um bom arquiteto empurra decisões para o lado "barato de mudar" sempre que possível — adiar é uma estratégia.

1.2 Atributos de qualidade (os "-ilities")

Requisitos funcionais dizem o que o sistema faz. Atributos de qualidade dizem quão bem — e é o que a arquitetura serve. Os principais:

AtributoPergunta que responde
EscalabilidadeAguenta 10×/100× a carga? A que custo?
DisponibilidadeQuanto tempo fora do ar por ano é tolerável? (99,9% = ~8,7h/ano)
Desempenho / latênciaResponde rápido o suficiente no p95/p99?
Confiabilidade / resiliênciaO que acontece quando uma dependência falha?
Manutenibilidade / evolutibilidadeQuão fácil é mudar sem quebrar? Quão rápido um time novo entende?
SegurançaSuperfície de ataque, isolamento, dados protegidos
ObservabilidadeDá para saber o que está acontecendo em produção?
CustoCabe no orçamento hoje e quando escalar?

1.3 Tudo é trade-off

A frase mais importante da apostila: "depende" é a resposta profissional. Não existe arquitetura "melhor" — existe a mais adequada às forças do seu contexto. Microservices compram escalabilidade e autonomia de time ao custo de complexidade operacional, latência de rede e consistência distribuída. Cache compra latência ao custo de invalidação e possível dado velho. Consistência forte compra correção ao custo de disponibilidade sob partição (ver apostila de NoSQL, teorema CAP).

⚠️ O maior erro de arquitetura: resolver problema que você não tem

Adotar microservices, Kafka, service mesh e multi-região com 3 desenvolvedores e 200 usuários é a receita clássica de matar um produto por over-engineering. A arquitetura certa para a maioria dos sistemas é a mais simples que atende os requisitos atuais e não impede os próximos passos previsíveis. Comece simples; adicione complexidade quando a dor for real.

1.4 O papel de quem faz arquitetura

💼 Mercado de trabalho

Domínio de arquitetura é o que destrava as faixas sénior / staff / principal e as vagas de "Solutions Architect". Nas entrevistas, a competência avaliada quase nunca é "você conhece o padrão X?", e sim "você raciocina sobre trade-offs e faz as perguntas certas antes de desenhar?". A resposta "depende — quais são os requisitos de consistência e escala?" vale mais que qualquer diagrama pronto.

✏️ Exercício 1 — Nomeie os atributos

Para cada sistema, quais 2–3 atributos de qualidade são dominantes (não "todos importam")? (a) sistema de trading de alta frequência; (b) app interno de RH de uma empresa de 500 pessoas; (c) plataforma de streaming de vídeo global; (d) prontuário eletrônico hospitalar.

Gabarito: (a) Latência (microssegundos) e determinismo; disponibilidade importa mas latência manda. (b) Manutenibilidade e custo baixo; escala é trivial. (c) Escalabilidade, disponibilidade e desempenho de entrega (throughput, CDN); custo por stream. (d) Confiabilidade, consistência/integridade dos dados, segurança/privacidade e auditabilidade; latência é secundária. O ponto: arquiteturas diferentes porque as forças dominantes são diferentes.

MÓDULO 02 · BÁSICO

Fundamentos: acoplamento, coesão e fronteiras

Objetivo: dominar os conceitos que valem em qualquer estilo — acoplamento e coesão, inversão de dependência, a Lei de Conway e a diferença entre complexidade essencial e acidental.

2.1 Acoplamento e coesão

Praticamente toda "boa arquitetura" é uma forma de buscar alta coesão dentro das fronteiras e baixo acoplamento entre elas. Tipos de acoplamento, do pior ao mais tolerável: de conteúdo (mexe nas entranhas do outro) → comum (estado global compartilhado) → de controle (passa flag que dita o comportamento) → de dados (troca só os dados necessários por uma interface).

2.2 Fronteiras e inversão de dependência

Uma fronteira é uma linha através da qual as dependências apontam em uma direção controlada. O princípio de inversão de dependência (o "D" de SOLID): módulos de alto nível (regra de negócio) não devem depender de módulos de baixo nível (banco, framework, API externa); ambos dependem de abstrações.

   SEM inversão:  [regra de negócio] ──depende──▶ [Postgres] [Stripe] [SendGrid]
                  (trocar de banco = reescrever a regra)

   COM inversão:  [regra de negócio] ──▶ [interface Repositório] ◀──implementa── [Postgres]
                  [regra de negócio] ──▶ [interface Pagamentos]  ◀──implementa── [Stripe]
                  (a regra não conhece a tecnologia; adapters são plugáveis e testáveis)

É a base de arquitetura hexagonal / ports & adapters / clean architecture (Módulo 3): o núcleo de domínio no centro, sem dependência de infraestrutura; tudo que é I/O é um adaptador na borda.

2.3 Lei de Conway

"Organizações projetam sistemas que copiam a estrutura de comunicação da própria organização." (Melvin Conway, 1967.) Consequências práticas:

2.4 Complexidade essencial × acidental

De "No Silver Bullet" (Fred Brooks): a complexidade essencial é a do problema em si (um sistema de logística é complexo). A acidental é a que você adiciona com ferramentas, camadas e abstrações mal escolhidas. Bom arquiteto ataca a acidental sem fingir que elimina a essencial. Toda camada, todo serviço, toda tecnologia nova é complexidade acidental que precisa se pagar.

💼 Mercado de trabalho

Perguntas: "O que é acoplamento e coesão, com exemplo?", "Explique inversão de dependência e por que ela ajuda a testar", "O que é a Lei de Conway e como você a usa a favor?", "Como você decide onde colocar uma fronteira entre módulos?" (por domínio de negócio / capacidade, alinhado ao time, minimizando o acoplamento que sobra).

✏️ Exercício 2 — Encontre o acoplamento ruim

Um módulo de Relatórios importa diretamente o modelo ORM de Pedidos, lê colunas específicas, e quando o time de Pedidos renomeia uma coluna, os relatórios quebram em produção. Que tipo de acoplamento é esse e como reduzir?

Gabarito: Acoplamento de conteúdo/estrutura — Relatórios depende do schema interno de Pedidos. Reduzir: Pedidos expõe uma interface estável (uma API, uma view de leitura, um evento com contrato versionado, ou um DTO) e Relatórios consome só isso. A mudança de coluna interna passa a ser invisível para fora. Se relatórios precisam de dados de vários domínios, considerar um modelo de leitura próprio alimentado por eventos (CQRS — Módulo 5).

MÓDULO 03 · INTERMEDIÁRIO

Estilos arquiteturais

Objetivo: conhecer os estilos mais usados — o que cada um otimiza, o que custa, e quando escolher — sem tratar nenhum como padrão-ouro universal.

3.1 Monólito (e o monólito modular)

Uma única unidade de deploy. Não é sinônimo de bagunça: um monólito modular tem fronteiras internas explícitas (módulos com interfaces claras, sem acesso ao banco um do outro), roda como um processo, e é a escolha certa para a esmagadora maioria dos sistemas no início e por muito tempo.

3.2 Camadas (layered / n-tier)

Organização horizontal: apresentação → aplicação → domínio → persistência. Familiar e fácil de ensinar. Risco: virar um "monólito em camadas" onde toda feature atravessa todas as camadas e a de domínio depende da de persistência (acoplamento invertido). Combine com inversão de dependência.

3.3 Hexagonal / Ports & Adapters / Clean

Organização concêntrica: domínio puro no centro; "portas" (interfaces) ao redor; "adaptadores" (HTTP, banco, filas, APIs externas) na borda, dependendo para dentro. Ótimo para testabilidade (o domínio roda sem infraestrutura) e para trocar tecnologia sem tocar a regra. É um padrão interno — combina com monólito modular ou com cada microserviço.

3.4 Microservices

Vários serviços pequenos, cada um com seu deploy, seu time e (idealmente) seu banco, comunicando-se por rede.

GanhaCusta
Escala independente por serviçoComplexidade operacional (dezenas de deploys, service discovery, observabilidade distribuída)
Autonomia de time (deploy sem coordenar)Consistência distribuída (sem transação ACID entre serviços — Módulo 5)
Isolamento de falha (um serviço cai, o resto segue)Latência e falha de rede em toda chamada
Liberdade tecnológica por serviçoTestes de integração e debugging mais difíceis; custo de infra maior
⚠️ Pré-requisitos para microservices darem certo

CI/CD maduro, automação de infra, observabilidade distribuída (tracing), cultura de on-call, e fronteiras de domínio bem entendidas. Sem isso, você troca a dor conhecida do monólito pela dor pior do monólito distribuído (serviços que precisam ser deployados juntos, chamadas síncronas em cadeia, um banco compartilhado escondido). Regra de ouro: comece monólito modular; extraia serviços quando a fronteira estiver óbvia e a dor for real.

3.5 Event-Driven Architecture (EDA)

Componentes reagem a eventos (fatos que aconteceram: PedidoPago, UsuárioCadastrado) publicados num broker (Kafka, RabbitMQ, SNS/SQS). O produtor não sabe (nem espera) quem consome.

3.6 Serverless (FaaS)

Funções que sobem sob demanda, escalam a zero, você paga por invocação. Ótimo para cargas intermitentes, glue code, processamento orientado a evento, e times que não querem gerir servidor. Custa: cold start, limites de execução, lock-in, e custo alto em volume constante. Frequentemente combinado com EDA.

3.7 Um guia rápido de escolha

SituaçãoComece com
Produto novo, time pequeno, requisitos em descobertaMonólito modular + hexagonal internamente
Monólito crescendo, times pisando no pé um do outro, partes com escala muito diferenteExtrair microserviços nas fronteiras óbvias (strangler — Módulo 9)
Muitos consumidores reagindo aos mesmos fatos; integrações que mudam sempreAdicionar EDA para os fluxos assíncronos
Cargas esporádicas, picos imprevisíveis, glue entre serviçosServerless para essas peças
💼 Mercado de trabalho

Perguntas: "Monólito ou microservices para uma startup em estágio inicial?" (monólito modular — a resposta oposta é red flag), "O que é um monólito distribuído e por que é o pior dos mundos?", "Trade-offs de EDA", "O que são ports and adapters?". Demonstrar que você não vai de microservices por reflexo é um sinal forte de senioridade.

✏️ Exercício 3 — Escolha e justifique

Uma fintech tem um monólito de 6 anos. O módulo de "cálculo de risco" precisa escalar 20× em picos e é mantido por um time dedicado; o resto do sistema tem carga estável. Qual sua recomendação?

Gabarito: Extrair apenas "cálculo de risco" como serviço independente (padrão strangler): a fronteira é clara (domínio coeso, time dedicado, exigência de escala distinta), e o ganho — escalar só essa peça, deploy independente — justifica o custo. Manter o resto como monólito modular. Comunicação: síncrona se o cálculo é no caminho da requisição (com timeout/circuit breaker), ou assíncrona se puder ser feito fora do fluxo. Evitar a tentação de "já que vamos, quebrar tudo em microserviços".

MÓDULO 04 · INTERMEDIÁRIO

Comunicação entre serviços

Objetivo: escolher entre síncrono e assíncrono com consciência do acoplamento temporal, e conhecer os padrões de coordenação (orquestração vs coreografia) e de borda (gateway, BFF).

4.1 Síncrono × assíncrono

Síncrono (REST, gRPC)Assíncrono (mensagem/evento)
ModeloChamo e espero a respostaPublico e sigo em frente; alguém processa depois
Acoplamento temporalAlto: o outro serviço precisa estar de pé agoraBaixo: o broker guarda a mensagem até dar
Latência do usuárioSoma a latência de toda a cadeiaResponde já; o trabalho acontece em background
FalhaFalha propaga na hora (precisa de timeout, retry, circuit breaker)Broker absorve; reprocessa; mas exige idempotência e tratamento de ordem
Bom paraPreciso da resposta para continuar (consultar saldo, validar)Notificar, propagar mudança, processar pesado, integrar N consumidores
⚠️ Cadeias síncronas são bombas de disponibilidade

Se A chama B que chama C que chama D, todos síncronos, a disponibilidade de A é o produto das disponibilidades (4 serviços a 99,9% ⇒ ~99,6%), e a latência é a soma. Cada salto síncrono adicionado piora as duas coisas. Prefira assíncrono quando a resposta imediata não for necessária, e achate as cadeias.

4.2 REST × gRPC × GraphQL (resumo)

4.3 Coordenação: orquestração × coreografia

Quando um processo de negócio cruza vários serviços (ex.: "fazer um pedido" toca estoque, pagamento, entrega):

OrquestraçãoCoreografia
Um serviço "maestro" comanda: chama estoque, depois pagamento, depois entrega, e trata as falhasCada serviço reage a eventos: PedidoCriado → estoque reserva e emite EstoqueReservado → pagamento cobra e emite PagamentoAprovado → …
+ Fluxo explícito e fácil de entender/monitorar; lógica centralizada+ Desacoplado; fácil adicionar um passo/consumidor novo
− O maestro vira ponto de acoplamento e de falha; pode virar "deus"− Ninguém "vê" o fluxo inteiro; difícil depurar; risco de ciclos de evento

Regra prática: processos de negócio complexos e com muitas regras de compensação ⇒ orquestração (com um motor de workflow tipo Temporal/Camunda se justificar). Propagação de fatos simples ⇒ coreografia.

4.4 Padrões de borda

💼 Mercado de trabalho

Perguntas: "Quando usar comunicação assíncrona em vez de síncrona?", "Por que cadeias de chamadas síncronas prejudicam a disponibilidade?", "Orquestração ou coreografia para um checkout?", "O que é um BFF e que problema resolve?". Falar em "acoplamento temporal" e no efeito multiplicativo na disponibilidade sinaliza profundidade.

✏️ Exercício 4 — Redesenhe o fluxo

No cadastro de usuário, o endpoint faz, de forma síncrona e em sequência: grava no banco, chama o serviço de e-mail de boas-vindas, chama o CRM, chama o serviço de analytics, chama o de antifraude. O p95 do cadastro é 4,2 s e cai quando o CRM está lento. Redesenhe.

Gabarito: Síncrono, só o essencial para responder ao usuário: gravar no banco e, se necessário, a checagem antifraude que bloqueia o cadastro. Tudo o mais (e-mail de boas-vindas, CRM, analytics) vira assíncrono: o endpoint publica um evento UsuárioCadastrado (via outbox para não ter dual-write — Módulo 5) e responde. Consumidores independentes tratam e-mail, CRM e analytics, com retry e idempotência; se o CRM cai, a fila segura e reprocessa, sem afetar o cadastro. p95 despenca para ~o tempo de um insert.

MÓDULO 05 · INTERMEDIÁRIO

Dados em sistemas distribuídos

Objetivo: entender por que "banco por serviço" quebra as transações, e os padrões que resolvem — outbox, saga, CQRS, event sourcing — junto com consistência eventual e idempotência.

5.1 Database per service e o fim do JOIN

Em microservices, cada serviço é dono do seu banco e ninguém mais o acessa direto (senão o acoplamento volta pela porta dos fundos). Consequências: acabou o JOIN entre domínios, acabou a transação ACID que abrange dois serviços, e "montar uma tela com dados de 4 serviços" vira um problema de arquitetura.

5.2 O problema do dual-write

"Vou gravar no meu banco e publicar o evento no Kafka." Se a segunda operação falha (ou o processo morre entre as duas), os sistemas divergem — e não há transação entre banco e broker.

💡 Transactional Outbox — o padrão que resolve

Na mesma transação do banco em que você grava o dado, você insere o evento numa tabela outbox. Um processo separado (poller ou CDC — ver apostila de Engenharia de Dados) lê a outbox e publica no broker, marcando como enviado. Se algo falhar, a transação inteira reverte ou o evento é reenviado. Entrega at-least-once garantida, sem dual-write. Consumidores precisam ser idempotentes.

5.3 Saga — transações que cruzam serviços

Uma saga é uma sequência de transações locais, cada uma num serviço, com uma transação de compensação para desfazer cada passo se um posterior falhar (não há rollback distribuído).

Pedido:  reservar estoque ──▶ cobrar pagamento ──▶ agendar entrega
                                     │ falhou!
                                     ▼
compensação:  estornar pagamento ◀── liberar estoque   (desfaz na ordem inversa)

5.4 CQRS

Command Query Responsibility Segregation: separar o modelo de escrita (comandos, normalizado, com regras de negócio) do modelo de leitura (queries, desnormalizado, otimizado para as telas). O modelo de leitura é atualizado a partir de eventos do modelo de escrita.

5.5 Event Sourcing avançado

Em vez de guardar o estado atual, guarde a sequência de eventos que levou a ele (ContaAberta, Depositado 100, Sacado 30 ⇒ saldo 70). O estado é uma projeção (fold) dos eventos.

5.6 Consistência eventual e idempotência

💼 Mercado de trabalho

Bloco muito cobrado em entrevista sénior: "Como manter consistência entre dois serviços sem transação distribuída?" (saga + outbox), "O que é o problema do dual-write?", "O que é CQRS e quando vale a pena?", "Diferença entre CQRS e event sourcing", "Como garantir idempotência num consumidor de fila?". Citar outbox sem que perguntem, ao falar de eventos, é sinal de quem já sofreu na prática.

✏️ Exercício 5 — Projete a consistência

Serviço de Pedidos e serviço de Estoque, bancos separados. Ao criar um pedido, o estoque precisa ser decrementado; se o pagamento falhar depois, o estoque volta. Descreva o desenho.

Gabarito: Saga (coreografada, poucos passos): (1) Pedidos grava o pedido como PENDENTE e, na mesma transação, insere PedidoCriado na outbox; um relay publica o evento. (2) Estoque consome PedidoCriado (idempotente, via chave = pedido_id), reserva/decrementa e emite EstoqueReservado (ou EstoqueInsuficiente). (3) Pagamento consome EstoqueReservado, cobra, emite PagamentoAprovado ou PagamentoRecusado. (4) Em PagamentoRecusado ou EstoqueInsuficiente, Pedidos marca o pedido como CANCELADO e Estoque consome o evento de cancelamento para compensar (liberar a reserva). Tudo idempotente; consistência eventual assumida na UX ("pedido em processamento").

MÓDULO 06 · AVANÇADO

Escalabilidade e performance

Objetivo: as ferramentas para o sistema aguentar mais carga — stateless + escala horizontal, balanceamento, caching, particionamento/sharding e controle de fluxo.

6.1 Vertical × horizontal

Tornar stateless: sessão em cookie assinado ou em store compartilhado (Redis); uploads e arquivos em object storage; nada de "afinidade de sessão" como muleta permanente.

6.2 Balanceamento de carga

6.3 Caching — a maior alavanca de performance

NívelExemplo
Cliente / browserCache-Control, ETag
CDN / edgeAssets estáticos, respostas cacheáveis perto do usuário
Gateway / reverse proxyRespostas de GET idempotentes
Aplicação (distribuído)Redis/Memcached: resultado de query, objeto montado, sessão
BancoBuffer pool, materialized views

6.4 Particionamento e sharding

6.5 Controle de fluxo: filas, back-pressure e limites

💼 Mercado de trabalho

Perguntas: "Como você escala um serviço web?" (stateless + horizontal + LB + cache + read replicas + shard quando a escrita apertar), "Estratégias e armadilhas de cache", "Como escolher uma shard key?", "O que é back-pressure?", "Por que um sistema a 95% de utilização fica lento?" (teoria de filas). Este módulo é o coração das perguntas de estimativa da entrevista de system design.

✏️ Exercício 6 — Ataque o gargalo

Uma API de catálogo faz 5.000 req/s, cada uma roda a mesma query pesada de "produtos em destaque" (muda 2×/dia). O banco está a 90% de CPU e o p99 subiu para 900 ms. Ordene as ações.

Gabarito: (1) Cache do resultado de "produtos em destaque" (Redis ou até em memória do processo), TTL de alguns minutos ou invalidação nos 2 momentos de mudança — resolve quase tudo na hora, pois é a mesma resposta para todos. (2) Se ainda houver pressão de leitura, read replicas e distribuir as queries. (3) CDN/edge cache para a resposta se ela for pública. (4) Revisar índices/plano da query. (5) Só então pensar em escalar o banco verticalmente ou shardar — provavelmente desnecessário depois do cache. Lição: identificar que 5.000 req/s pedem a mesma coisa muda tudo.

MÓDULO 07 · AVANÇADO

Confiabilidade e resiliência

Objetivo: projetar para a falha — porque em escala, algo está sempre quebrado. SPOF, redundância, os padrões de tolerância a falha, e recuperação de desastre.

7.1 A premissa: tudo falha

Discos morrem, redes particionam, dependências ficam lentas, deploys quebram, uma zona de disponibilidade cai. Arquitetura resiliente não tenta impedir toda falha — ela contém o raio de explosão e degrada com elegância em vez de cair por inteiro.

7.2 Elimine pontos únicos de falha (SPOF)

7.3 Os padrões de tolerância a falha

PadrãoProblema que resolve
TimeoutNunca espere para sempre. Toda chamada de rede tem timeout agressivo e sensato.
Retry com backoff + jitterFalha transitória se resolve tentando de novo — com espera crescente e aleatória para não criar "thundering herd". Só para operações idempotentes.
Circuit breakerSe um serviço está falhando muito, pare de chamá-lo por um tempo (circuito "aberto"), retorne fallback na hora, e teste periodicamente se voltou. Evita esgotar threads esperando um serviço morto.
BulkheadIsolar recursos (pools de thread/conexão) por dependência, para que a lentidão de uma não afogue o serviço inteiro.
Fallback / degradação graciosaSem recomendações personalizadas? Mostre as populares. Sem o serviço de avaliações? Esconda a seção. O core continua de pé.
Load sheddingSob sobrecarga, rejeitar parte das requisições rápido (com 429/503) para proteger a capacidade de atender o resto.
Idempotency keyCliente manda uma chave única; o servidor deduplica retries de operações que mudam estado (pagamentos!).
⚠️ Retry mal feito derruba o sistema

Retry sem backoff + sem jitter + sem circuit breaker, feito por milhares de clientes ao mesmo tempo, transforma um soluço de 2 segundos numa avalanche que mantém o serviço no chão (retry storm). E retry em operação não idempotente cobra o cartão duas vezes. Retry é uma faca de dois gumes — use com backoff exponencial, jitter, teto de tentativas e circuit breaker.

7.4 Recuperação de desastre

💼 Mercado de trabalho

Perguntas: "O que é um circuit breaker e quando ele 'abre'?", "Como fazer retry corretamente?" (idempotente + backoff exponencial + jitter + teto + circuit breaker), "O que é degradação graciosa, com exemplo?", "Diferença entre RTO e RPO", "Como você evita um retry storm?". Em system design, "e se este componente cair?" é pergunta garantida.

✏️ Exercício 7 — Blinde a dependência

Sua página de produto chama um serviço externo de recomendações. Ele às vezes fica com 3 s de latência e derruba o p99 da página inteira; quando ele cai de vez, a página trava esperando. Aplique os padrões.

Gabarito: (1) Timeout curto (ex.: 200–300 ms) na chamada de recomendações — a página não pode refém dela. (2) Fallback: no timeout ou erro, mostrar recomendações genéricas/populares (cacheadas) ou esconder a seção. (3) Circuit breaker: se a taxa de erro/timeout passar de um limiar, parar de chamar por 30–60 s e servir só o fallback, testando de volta aos poucos. (4) Bulkhead: pool de conexões dedicado para recomendações, para não consumir as threads do resto. (5) Tornar o carregamento das recomendações assíncrono no front (a página renderiza sem elas e preenche depois). Resultado: recomendações viram um "extra" que nunca compromete o core.

MÓDULO 08 · AVANÇADO

A entrevista de System Design

Objetivo: um framework replicável para conduzir uma entrevista de projeto de sistema, com estimativas de capacidade e um passo a passo aplicado a exemplos clássicos.

8.1 O que o entrevistador avalia

Não é a "resposta certa" (não existe). É: você esclarece requisitos antes de desenhar? Estrutura o raciocínio ou pula direto para a solução? Justifica escolhas com trade-offs? Conhece os números (o que cabe em RAM, latência de disco vs rede, QPS por máquina)? Identifica gargalos e sabe aprofundar quando pedem? Comunica com clareza?

8.2 O framework (≈45 min)

PassoTempoO que fazer
1. Requisitos~5 minFuncionais (features do MVP), não-funcionais (escala, latência, consistência, disponibilidade), e o que está fora do escopo. Confirme com o entrevistador.
2. Estimativas~5 minUsuários (DAU), QPS médio e de pico (leitura vs escrita), volume de dados/dia e em 5 anos, banda, memória de cache. Ordens de grandeza, não precisão.
3. API~3 minOs principais endpoints/contratos — define as fronteiras.
4. Modelo de dados~5 minEntidades, relacionamentos, escolha de datastore (SQL? KV? busca? blob?) justificada pelo padrão de acesso.
5. Desenho de alto nível~10 minCaixas e setas: clientes → LB → serviços → dados → cache → filas → workers. O caminho de leitura e o de escrita.
6. Aprofundar~10 minOnde o entrevistador apontar: o componente crítico, o gargalo de escala, a consistência, o caso de falha.
7. Gargalos & evolução~5 minSPOFs, hot spots, o que quebra primeiro em 10×, como monitorar, o que você faria diferente com mais tempo.

8.3 Números que você precisa ter na cabeça

Latências de referência (ordem de grandeza):
  ler 1 MB da memória ........... ~0,25 ms
  SSD: leitura aleatória ........ ~0,1 ms
  round-trip na mesma região .... ~0,5 ms
  round-trip entre continentes .. ~150 ms

Capacidade (folgada, "de guardanapo"):
  QPS por instância de app ...... ~1.000 (varia MUITO com a carga)
  linhas que cabem em RAM ....... milhões a dezenas de milhões
  1 dia = 86.400 s  →  1M eventos/dia ≈ ~12/s médio  (pico pode ser 5–20× a média)

Regra do 80/20 e leitura-pesada:
  a maioria dos sistemas de produto é 10:1 a 1000:1 leitura:escrita → cache e réplicas mandam

8.4 Padrões que reaparecem

💡 Erros que reprovam

Pular a fase de requisitos e começar a desenhar; não fazer estimativa nenhuma; jogar todo buzzword sem justificar; ir de microservices + Kafka + K8s para um problema que um monólito + Postgres resolveria; ficar em silêncio (é uma conversa, não uma prova escrita); não admitir trade-offs ("minha solução não tem desvantagem" é sempre falso).

💼 Mercado de trabalho

A entrevista de system design é decisiva para vagas pleno-sênior+ em produto e Big Tech. Prepare-se resolvendo ~10–15 problemas clássicos em voz alta, cronometrado, seguindo sempre o mesmo framework. Estude os números da seção 8.3 até serem automáticos. E pratique a comunicação: desenhe enquanto fala, cheque premissas, conduza.

✏️ Exercício 8 — Esboço em 10 minutos: encurtador de URL

Faça o esqueleto: requisitos, estimativa, API, dados, alto nível, um gargalo.

Gabarito (esboço): Requisitos: encurtar URL, redirecionar, (opcional) URL custom, analytics de cliques; 100M links/mês, leitura:escrita ~100:1, redirecionamento < 50 ms, alta disponibilidade (link quebrado é péssimo). Fora: contas, edição. Estimativa: ~40 escritas/s média, ~4.000 leituras/s pico; 100M/mês × 5 anos ≈ 6B registros × ~500 B ≈ 3 TB. API: POST /urls {long_url} → {short}; GET /{short} → 302. Dados: KV store (short → long_url, created_at), sharded por hash do short; analytics em store separado, escrito assíncrono. Alto nível: cliente → LB → serviço de escrita (gera short via base62 de um contador distribuído ou hash+colisão) → KV; redirecionamento: LB → serviço de leitura → cache (Redis, hit rate altíssimo porque poucos links concentram tráfego) → KV no miss → 302; evento de clique → fila → worker → analytics. Gargalo: leitura — resolvido por cache + réplicas; a geração de id não pode colidir nem ser um SPOF (usar faixas de contador pré-alocadas por instância, ou hash com verificação).

MÓDULO 09 · MUITO AVANÇADO

Arquitetura na prática: documentar, avaliar e evoluir

Objetivo: as práticas que mantêm a arquitetura viva e saudável ao longo dos anos — ADRs, C4, fitness functions, avaliação, anti-padrões e migração incremental.

9.1 Documentar decisões: ADRs

Um Architecture Decision Record é um arquivo curto (uma página) no repositório, versionado com o código, que registra uma decisão: contexto, opções consideradas, decisão tomada, consequências (boas e ruins), e status (proposto/aceito/substituído). O valor não é o documento — é forçar o raciocínio e deixar rastro para quem chegar depois perguntar "por que isto é assim?".

# ADR-014: Usar outbox para publicação de eventos de domínio

## Status: Aceito (2026-03)
## Contexto
Precisamos publicar eventos quando um pedido muda de estado. Dual-write
(gravar no banco + publicar no broker) pode divergir em falha parcial.
## Decisão
Inserir o evento numa tabela `outbox` na mesma transação do banco; um relay
CDC publica no broker.
## Consequências
+ Sem perda/divergência de evento; entrega at-least-once.
+ Consumidores DEVEM ser idempotentes (aceito).
- Uma peça a mais (o relay) para operar e monitorar.

9.2 Comunicar a estrutura: o modelo C4

Quatro níveis de zoom, cada um um diagrama simples: Contexto (o sistema e quem interage com ele) → Contêineres (apps, serviços, bancos — as unidades de deploy) → Componentes (dentro de um contêiner) → Código (raramente necessário). Substitui o diagrama "UML gigante que ninguém entende" por uma hierarquia legível. Poucas caixas, legendas claras, uma história por diagrama.

9.3 Arquitetura evolutiva e fitness functions

9.4 Avaliar uma arquitetura

ATAM (Architecture Tradeoff Analysis Method) é o método formal: a partir de cenários de atributos de qualidade ("o tráfego dobra na Black Friday", "um data center cai", "adicionamos um novo tipo de pagamento"), avalia-se como a arquitetura responde, quais os pontos de sensibilidade e os trade-offs. Mesmo informalmente, "rodar cenários" é a melhor forma de estressar um desenho antes de construí-lo.

9.5 Anti-padrões

Anti-padrãoSintoma
Big Ball of MudSem fronteiras; tudo acessa tudo; mudar qualquer coisa quebra outra
Monólito distribuídoServiços separados que precisam ser deployados juntos, chamam-se em cadeia síncrona e compartilham banco
Golden Hammer"Resolvemos tudo com Kafka / com microserviços / com o padrão X"
Arquitetura de currículoTecnologia escolhida para o CV do arquiteto, não para o problema
Acoplamento por banco compartilhadoVários serviços lendo/escrevendo as mesmas tabelas
Camada anêmica de abstraçãoWrappers que só repassam chamadas, adicionando indireção sem valor

9.6 Migração incremental: Strangler Fig

Nunca faça o "big bang rewrite". O padrão strangler fig (Fowler): coloque uma fachada (proxy/gateway) na frente do sistema legado; construa cada capacidade nova (ou reescrita) atrás dela; roteie o tráfego daquela função para o novo componente; repita até o legado "estrangulado" poder ser desligado. Combine com shadow traffic (o novo processa em paralelo sem servir) e feature flags para reduzir risco.

💼 Mercado de trabalho

Perguntas: "Como você documenta decisões de arquitetura?" (ADRs no repo), "O que é o modelo C4?", "O que é uma fitness function arquitetural?", "Como migrar um monólito legado sem parar o negócio?" (strangler fig + shadow + flags), "Cite anti-padrões de arquitetura que você já viu". Ter uma história real de migração incremental bem conduzida é ouro em entrevista sénior.

✏️ Exercício 9 — Escreva o ADR

Seu time decidiu adotar CQRS só na área de "relatórios e dashboards", mantendo o resto CRUD normal. Escreva o ADR (contexto, decisão, consequências).

Gabarito (esboço): Contexto: as telas de relatório fazem JOINs pesados entre 5 tabelas de domínios diferentes, degradam o banco transacional e acoplam o time de relatórios ao schema dos outros. Decisão: introduzir um read model dedicado a relatórios, desnormalizado, atualizado por eventos de domínio (consistência eventual, atraso aceitável de minutos); o caminho de escrita e o CRUD das outras áreas permanecem inalterados. Consequências: + leitura de relatório fica trivial e não pesa no banco transacional; + relatórios deixam de depender do schema interno dos outros domínios. − mais uma base e um pipeline de projeção para operar; − dados de relatório podem estar alguns minutos atrás (aceito pelo negócio); − necessidade de reprocessar projeções em caso de bug. Status: Aceito.

MÓDULO 10 · CARREIRA

Mercado de trabalho: roadmap, entrevistas e portfólio

Objetivo: converter o conteúdo dos módulos anteriores em aprovação — perfis, roadmap, banco de perguntas com respostas e como demonstrar competência de arquitetura.

10.1 Onde arquitetura pesa na carreira

Nível / papelO que se espera de arquitetura
Pleno → SéniorProjetar um serviço/feature com boas fronteiras; passar na entrevista de system design; raciocinar sobre trade-offs
Staff / Principal EngineerArquitetura de vários sistemas, decisões que atravessam times, mentoria, ADRs, reduzir risco cedo
Solutions / Cloud ArchitectDesenhar soluções sobre uma stack de nuvem, com foco em requisitos não-funcionais e custo
Engineering Manager (técnico)Guiar decisões de arquitetura do time, alinhar com a Lei de Conway

10.2 Roadmap de estudo (8–10 semanas)

SemanasFocoPrática
1–2Fundamentos: atributos de qualidade, acoplamento/coesão, SOLID, Conway (Módulos 1–2)Refatorar um projeto seu introduzindo ports & adapters no núcleo
3Estilos: monólito modular, hexagonal, microservices, EDA, serverless (Módulo 3)Escrever um comparativo justificado para um sistema real que você conhece
4Comunicação: sync/async, orquestração/coreografia, gateway/BFF (Módulo 4)Redesenhar um fluxo síncrono do seu trabalho como assíncrono com outbox
5Dados distribuídos: outbox, saga, CQRS, event sourcing, idempotência (Módulo 5)Implementar uma saga simples (2–3 serviços) com compensação
6Escala e performance: stateless, LB, cache, sharding, back-pressure (Módulo 6)Adicionar cache-aside com invalidação a um endpoint e medir
7Resiliência: circuit breaker, retry, bulkhead, degradação, RTO/RPO (Módulo 7)Blindar uma dependência externa com timeout + fallback + breaker
8–9System design: framework + 12 problemas clássicos cronometrados (Módulo 8)Resolver em voz alta, gravar-se, revisar contra soluções de referência
10Prática: ADRs, C4, fitness functions, strangler (Módulo 9); portfólioDocumentar um sistema seu com C4 + 5 ADRs; publicar

10.3 Banco de perguntas de entrevista (com a resposta que aprova)

Pleno — "Monólito ou microservices para um produto novo?"

Monólito modular. Um produto novo tem requisitos em descoberta, time pequeno e sem a maturidade operacional (CI/CD, observabilidade distribuída, on-call) que microservices exigem. Monólito modular dá fronteiras internas claras, transações locais e refatoração barata; extrai-se um serviço quando a fronteira fica óbvia e a dor (escala divergente, times pisando no pé) for real. Ir de microservices cedo é o over-engineering clássico.

Pleno — "Como dois serviços mantêm consistência sem transação distribuída?"

Saga: sequência de transações locais, cada uma com uma compensação para desfazer se um passo posterior falhar. Os eventos que disparam os passos são publicados via transactional outbox (gravados na mesma transação do dado) para não haver dual-write. Consumidores idempotentes. Aceita-se consistência eventual e projeta-se a UX para ela.

Pleno/sénior — "Explique circuit breaker, retry e bulkhead"

Retry: repetir uma operação idempotente que falhou por causa transitória, com backoff exponencial + jitter e teto de tentativas. Circuit breaker: após muitas falhas de uma dependência, parar de chamá-la por um tempo e retornar fallback imediato, testando a volta aos poucos — evita esgotar threads esperando um serviço morto e dá tempo para ele se recuperar. Bulkhead: isolar pools de recursos por dependência para que a lentidão de uma não afogue o serviço todo. Os três juntos evitam que uma falha local vire falha total.

Sénior — "Como você escalaria um serviço de leitura pesada?"

Tornar stateless e escalar horizontalmente atrás de um LB; adicionar cache em vários níveis (CDN para o público, Redis para objetos/queries, com TTL+jitter e cuidado com stampede e hot key); read replicas do banco distribuindo as leituras; desnormalizar / read model (CQRS) para as telas caras; só shardar a escrita quando ela virar gargalo. Medir onde está o gargalo antes de agir — muitas vezes 5.000 req/s pedem a mesma resposta e um cache resolve.

Sénior — "O que é um monólito distribuído e como evitar?"

São "microserviços" que na prática têm as desvantagens do distribuído sem os benefícios: precisam ser deployados juntos, chamam-se em cadeias síncronas longas, compartilham banco, e uma mudança obriga a mexer em vários. Evita-se colocando fronteiras por domínio de negócio (não por camada técnica), dando a cada serviço seu próprio dado, preferindo comunicação assíncrona, e não extraindo serviços antes de a fronteira estar clara.

Sénior — "Como migrar um monólito legado crítico?"

Strangler fig: fachada na frente do legado; cada capacidade nova ou reescrita é construída atrás dela; roteia-se o tráfego daquela função para o novo componente; repete-se até desligar o legado. Reduz risco com shadow traffic (novo processa em paralelo sem servir), feature flags e rollout gradual. Nunca big-bang rewrite — a taxa de fracasso é altíssima e o negócio não pode parar.

Armadilha — "Qual a melhor arquitetura?"

Não existe — depende dos atributos de qualidade dominantes do sistema e do contexto (time, prazo, escala real, orçamento). A pergunta certa é "quais forças mandam aqui?" e a resposta é a arquitetura mais simples que atende os requisitos atuais sem impedir os próximos passos previsíveis. Toda escolha é trade-off; quem diz que sua solução não tem desvantagem está errado.

10.4 Como demonstrar competência de arquitetura

  1. Documentar um sistema real (seu, de trabalho anonimizado, ou open source) com diagramas C4 e uma série de ADRs explicando as decisões e os trade-offs. Publicar.
  2. Estudo de system design escrito: escolher um produto conhecido, aplicar o framework do Módulo 8 num artigo, com estimativas e alternativas.
  3. Projeto que mostra um padrão distribuído: uma saga com compensação, ou CQRS com read model alimentado por eventos, ou um serviço com circuit breaker + outbox — pequeno, mas correto e explicado.
  4. Refatoração documentada: pegar um projeto acoplado e mostrar o "antes/depois" ao introduzir fronteiras e inversão de dependência, com fitness functions garantindo as regras.
  5. Contribuir em discussões de design (RFCs) de projetos open source.

10.5 Leituras de referência

🏁 Síntese final da apostila

Quatro ideias sustentam arquitetura: (1) arquitetura é o conjunto de decisões caras de mudar — e adiar as reversíveis é estratégia; (2) tudo é trade-off, não existe "melhor", e "depende — quais os requisitos?" é a resposta profissional; (3) busque alta coesão dentro das fronteiras, baixo acoplamento entre elas, alinhadas aos times (Conway); (4) comece simples e adicione complexidade só quando a dor for real — o distribuído resolve problemas reais e cria outros. As tecnologias mudam a cada ciclo; esses princípios, não.