🧱 Monolito
Single deployable unit — o ponto de partida inteligente. O Shopify processa US$175B/ano em GMV com um monolito Ruby on Rails.
O que é um Monolito?
Aplicação onde todo o código roda como uma única unidade deployável. Um processo, um banco, um deploy. É o estilo mais simples e frequentemente o mais eficiente para começar.
Vantagens: Simplicidade operacional, debugging direto, transações ACID nativas, time pequeno mantém tudo, latency mínima entre componentes.
Quando usar: MVP, startups, times < 20 devs, domínio bem definido, quando não se sabe ainda as fronteiras do sistema.
Monolitos em Números
- •Começar com monolito em projetos novos
- •Manter separação interna de módulos desde o dia 1
- •Usar monolito quando o domínio ainda não é claro
- •Investir em testes e CI/CD sólido
- •Big ball of mud — sem estrutura interna
- •Monolito com 50+ devs sem modularização
- •Deploy de 2h porque tudo está acoplado
- •Ignorar sinais de que o monolito precisa evoluir
🧩 Monolito Modular
O melhor dos dois mundos. O Basecamp usa módulos independentes dentro do mesmo app Rails — cada domínio (projects, messages, files) é isolado.
Bounded Contexts Internos
Um monolito modular mantém limites claros entre módulos. Cada módulo tem sua interface pública, seus dados privados, mas tudo roda no mesmo processo. Você ganha modularidade sem a complexidade distribuída.
Regra de ouro: Módulo A não acessa tabelas do Módulo B diretamente — usa a interface pública.
Como Modularizar seu Monolito
- 1.Identifique bounded contexts no seu domínio (pagamentos, usuários, catálogo)
- 2.Crie pastas/namespaces por módulo com interfaces públicas explícitas
- 3.Use linters/regras para impedir imports cruzados entre módulos
- 4.Cada módulo tem seus próprios testes unitários e de integração
De Monolito a Modular
Fase 1 — Mapeamento
Identifique domínios e dependências. Desenhe o grafo de acoplamento atual.
Fase 2 — Extração de Interfaces
Crie interfaces públicas para cada módulo. Substitua acessos diretos por chamadas à interface.
Fase 3 — Isolamento de Dados
Cada módulo controla suas tabelas. Proibir JOINs entre módulos.
Fase 4 — Enforce com Tooling
Linters, ArchUnit, dependency-cruiser para impedir regressões de acoplamento.
🔗 Microsserviços
Autonomia de times, não de tecnologia. A Amazon migrou para microsserviços quando tinha 1000+ devs e deploys bloqueavam uns aos outros.
Independência de Deploy
Cada serviço é independente: deploy próprio, banco próprio, equipe própria. Comunicação via API ou mensageria. O objetivo principal é permitir que times diferentes façam deploy sem coordenar entre si.
Princípio: “Microsserviços resolvem problemas organizacionais, não técnicos.”
Componentes: API Gateway, Service Discovery, Circuit Breaker, Service Mesh, Database per Service
Complexidade Operacional
Microsserviços trocam complexidade de código por complexidade operacional. Você precisa lidar com: rede, latência, consistência eventual, tracing distribuído, deploy de dezenas/centenas de serviços.
Prerrequisitos mínimos: CI/CD automatizado, monitoramento centralizado (Datadog, Grafana), tracing distribuído (Jaeger, Zipkin), infraestrutura como código. Se você não tem isso, não vá para microsserviços.
- •Usar quando times precisam de deploy independente
- •Database per service — cada serviço é dono dos seus dados
- •Investir em observabilidade antes de migrar
- •Definir contratos claros entre serviços (OpenAPI, protobuf)
- •Microsserviços com < 10 devs (overhead desnecessário)
- •Distributed monolith — serviços que precisam fazer deploy juntos
- •Nano-serviços — 200 funções com 10 linhas cada
- •Migrar sem observabilidade e CI/CD maduros
☁️ Serverless
Pay per execution, não por servidor. A Coca-Cola usa Lambda para processar vendas de máquinas automáticas — zero custo quando ninguém compra.
Functions as a Service (FaaS)
Funções que executam sob demanda, sem servidor dedicado. O cloud provider gerencia toda a infraestrutura: scaling, patching, availability. Você só escreve a lógica.
Providers: AWS Lambda, Cloudflare Workers, Vercel Functions, Google Cloud Functions, Azure Functions
Modelo: Stateless by design — cada execução é independente. Estado vive em serviços externos (DynamoDB, S3, Redis).
Cold Start e Limites
Quando Serverless é a Escolha Certa
- ✓Sim: Workloads spiky/imprevisíveis (webhooks, processamento de imagens, ETL)
- ✓Sim: APIs com tráfego variável (startups com zero a viral)
- ✓Sim: Event processing (S3 trigger, DynamoDB Streams)
- ✗Não: Workloads constantes e previsíveis (mais barato com EC2/containers)
- ✗Não: Aplicações que precisam de estado em memória ou conexões persistentes
📡 Event-Driven Architecture
Reagir em vez de perguntar. O Uber usa EDA para coordenar corridas — eventos disparam mapa, notificação, rota e billing em paralelo.
Pub/Sub e Event Sourcing
Componentes se comunicam através de eventos assíncronos. Um publica, vários consomem — desacoplamento máximo. Event Sourcing armazena cada mudança como evento imutável, permitindo reconstruir qualquer estado passado.
Padrões: Publish/Subscribe, Event Sourcing, CQRS (Command Query Responsibility Segregation)
Brokers: Apache Kafka, RabbitMQ, Redis Streams, AWS EventBridge, NATS
Throughput Comparison
Corrida no Uber — Event-Driven
Evento: ride.requested
Passageiro solicita corrida. Evento publicado no barramento.
Evento: driver.matched
Serviço de matching encontra motorista. Dispara eventos paralelos.
Paralelo: 4 consumers
Mapa (atualiza posição) + Notificação (push ao passageiro) + Rota (calcula ETA) + Billing (inicia tarifação).
Evento: ride.completed
Cobraça, avaliação, analytics — tudo reativo, sem polling.
🌐 Arquitetura em Camadas
Separação clássica de responsabilidades. A maioria dos apps Django/Rails segue esse padrão — controller, model, service layer.
Layers — Camadas com Responsabilidades Distintas
Sistema dividido em camadas horizontais: apresentação, lógica de negócio, acesso a dados. Cada camada só fala com a adjacente — a UI não acessa o banco diretamente.
Presentation
Controllers, Views, API endpoints
Business Logic
Services, Use Cases, Domain
Data Access
Repositories, ORM, Queries
- •Respeitar a dependência unidirecional (cima → baixo)
- •Service layer para lógica de negócio — não no controller
- •Usar para apps CRUD e domínios simples
- •Testar cada camada isoladamente
- •Anemic domain model — lógica toda no service, model vazio
- •Controller gordo — regras de negócio no controller
- •Pular camadas (UI acessando banco direto)
- •Usar para domínios complexos sem adaptar
Variações do Padrão
MVC: Model-View-Controller — o padrão clássico de Django, Rails, Spring MVC.
MVP: Model-View-Presenter — comum em apps Android legados. View passiva.
MVVM: Model-View-ViewModel — usado em WPF, SwiftUI, Vue.js. Two-way binding.
🏗️ Hexagonal / Clean Architecture
Negócio no centro, infraestrutura nas bordas. O Nubank migrou de Datomic para PostgreSQL sem tocar na lógica de negócio.
Ports and Adapters
O core da aplicação (regras de negócio) não depende de framework, banco ou API externa. Tudo é plugável via ports (interfaces que o domínio define) e adapters (implementações concretas).
Domain Core
Entities, Use Cases, regras puras
Ports
Interfaces definidas pelo domínio
Adapters
DB, HTTP, Queue, File System
Testabilidade
- •Usar quando lógica de negócio é complexa e central
- •Domínio puro — zero imports de framework
- •Dependency Inversion — domínio define interfaces
- •Ideal para fintechs, healthtechs, domínios regulados
- •Over-engineering em CRUDs simples
- •Criar 15 camadas para um TODO app
- •Ignorar o custo de indireção para times juniores
- •Domínio anêmico disfarçado de Clean Architecture