# ADR: Monolito Modular vs Microsserviços num Fintech Greenfield

Uma fintech em estágio inicial enfrenta a decisão clássica de arquitetura: partir direto para microsserviços ou construir um monolito modular primeiro. Este ADR examina as forças reais em jogo — tamanho de time, velocidade de validação, blast radius e custo operacional — e registra a decisão com suas consequências concretas.

- URL: https://fernando.moretes.com/studies/adr-monolito-modular-vs-microservices-fintech

- Markdown: https://fernando.moretes.com/studies/adr-monolito-modular-vs-microservices-fintech/study.md?lang=pt

- Type: Decisão (ADR)

- Company: Fintech (cenário)

- Domain: Arquitetura

- Status: accepted

- Date: 2026-01-15

- Tags: architecture, fintech, monolith, microservices, adr, greenfield, modular, trade-offs

- Reading time: 9 min

---

Microsserviços vendem bem em conferências. Monolitos modulares entregam produtos. Para uma fintech greenfield com time pequeno e domínio ainda não consolidado, a escolha da topologia de serviços é uma das decisões mais consequentes dos primeiros meses — e uma das mais frequentemente erradas por razões erradas.

## Ficha do Cenário

- **Empresa / Sistema:** Fintech greenfield (cenário composto, baseado em padrões recorrentes de mercado)
- **Estágio:** Pré-produto / MVP em construção
- **Tamanho do time de engenharia:** 4–7 engenheiros full-stack/backend, 1 tech lead
- **Domínio:** Pagamentos, onboarding KYC, gestão de contas — limites ainda em descoberta
- **Stack candidata:** Node.js / TypeScript ou Java 21, PostgreSQL, AWS (ECS ou EKS), eventual event bus
- **Restrições regulatórias:** Bacen / PCI-DSS nível 1 no horizonte; auditabilidade e segregação de dados sensíveis obrigatórias
- **Prazo para primeiro cliente pagante (estimativa):** 6–9 meses
- **Decisão registrada em:** Fase de inception / design sprint inicial

## O Contexto e as Forças em Jogo

Toda decisão de arquitetura é uma função das forças que atuam sobre o sistema num dado momento. Ignorar essas forças e aplicar um padrão porque ele é "moderno" ou porque a empresa de referência do setor o usa é uma das formas mais comuns de acumular dívida técnica prematura.

Neste cenário, as forças são claras e, em parte, contraditórias:

**Velocidade de validação do negócio.** A fintech ainda não sabe exatamente quais são seus bounded contexts reais. O domínio de pagamentos parece óbvio, mas os limites entre "conta", "carteira", "limite de crédito" e "transação" só se tornam claros depois de múltiplas iterações com clientes reais. Definir fronteiras de serviço antes de entender o domínio é o erro que Martin Fowler descreve com precisão cirúrgica em *MonolithFirst*: você distribui a complexidade antes de compreendê-la, e refatorar fronteiras entre microsserviços é ordens de magnitude mais custoso do que mover módulos dentro de um monolito.

**Tamanho e capacidade do time.** Com 4–7 engenheiros, cada hora de engenharia é escassa. Microsserviços introduzem overhead operacional imediato e não-trivial: pipelines CI/CD por serviço, service discovery, observabilidade distribuída (traces correlacionados, log aggregation, métricas por serviço), gestão de versões de API, eventual consistency entre serviços, e circuit breaking. Esse overhead existe independentemente de o sistema estar em produção ou não. Em times pequenos, esse custo é pago inteiramente antes de qualquer feature de negócio ser entregue.

**Blast radius e isolamento de falhas.** Aqui microsserviços têm vantagem real: uma falha no serviço de notificações não derruba o processamento de pagamentos. Num monolito, um bug em código de baixa criticidade pode — se mal isolado — afetar todo o processo. Esse argumento é legítimo, mas sua força depende diretamente de quão bem o monolito é estruturado internamente. Um monolito modular com separação clara de responsabilidades, tratamento de erros por módulo e circuit breakers internos reduz significativamente esse risco sem exigir infraestrutura distribuída.

**Requisitos regulatórios.** PCI-DSS e as normas do Bacen exigem segregação de dados de portadores de cartão, auditabilidade de acessos e, eventualmente, isolamento de rede para componentes que processam dados sensíveis. Isso cria pressão real por separação — mas essa separação pode ser endereçada com isolamento de módulo + criptografia em repouso e em trânsito + controles de acesso granulares dentro de um monolito, pelo menos no estágio inicial. A separação física em serviços distintos pode ser adiada sem violar requisitos regulatórios desde que os controles compensatórios estejam presentes.

## Opções Consideradas

### Opção A — Microsserviços desde o início

**Pros**
- Blast radius limitado por serviço desde o primeiro deploy
- Deploy independente por domínio de negócio
- Escalabilidade granular (escala só o que precisa)
- Alinhamento com times futuros (cada time owns um serviço)

**Cons**
- Overhead operacional imediato desproporcional ao tamanho do time
- Fronteiras de domínio ainda desconhecidas — risco alto de wrong cuts
- Latência de rede e eventual consistency introduzem bugs difíceis de reproduzir
- Custo de infraestrutura elevado antes de qualquer receita
- Observabilidade distribuída requer tooling maduro (Jaeger/X-Ray, centralized logging) desde o dia 1

**Verdict:** Rejeitada para fase inicial. Custo operacional e risco de wrong cuts superam os benefícios num time de 4–7 pessoas sem domínio consolidado.

### Opção B — Monolito modular com extração planejada

**Pros**
- Velocidade de desenvolvimento: sem overhead de rede, service mesh ou múltiplos pipelines
- Refatoração de fronteiras barata enquanto o domínio ainda está sendo descoberto
- Observabilidade simples no início (single process, structured logs, single trace context)
- Módulos bem definidos permitem extração cirúrgica quando o time crescer
- Custo de infraestrutura inicial significativamente menor

**Cons**
- Blast radius maior se módulos não forem disciplinadamente isolados
- Risco de acoplamento acidental crescer com o tempo ("big ball of mud" se não houver governança)
- Deploy de qualquer mudança requer rebuild e redeploy do artefato completo
- Escalabilidade granular não disponível sem extração prévia

**Verdict:** Aceita. Maximiza velocidade de validação e reduz risco de wrong cuts, com caminho claro para extração incremental.

### Opção C — Monolito não-estruturado (CRUD app)

**Pros**
- Máxima velocidade de prototipagem inicial

**Cons**
- Sem separação de responsabilidades: impossível extrair serviços depois sem reescrita
- Dívida técnica estrutural desde o primeiro commit
- Incompatível com requisitos de auditabilidade e segregação de dados regulatórios

**Verdict:** Rejeitada. Velocidade inicial não compensa a dívida estrutural e a incompatibilidade regulatória.

## Decisão Registrada

**Status:** accepted

**Contexto**

Time de 4–7 engenheiros construindo uma fintech greenfield com domínio de pagamentos e KYC ainda em descoberta. Prazo de 6–9 meses para primeiro cliente pagante. Requisitos regulatórios (Bacen, PCI-DSS) exigem segregação e auditabilidade, mas não impõem topologia de serviços específica no estágio inicial.

**Decisão**

Adotar monolito modular como arquitetura inicial, com módulos explicitamente delimitados por domínio (Payments, Accounts, KYC, Notifications, Auth), interfaces internas formalizadas via ports/adapters (Hexagonal Architecture), banco de dados único com schemas separados por módulo, e critérios pré-definidos para extração de serviços. A extração de um módulo em microsserviço só será iniciada quando: (1) o bounded context estiver estável por pelo menos dois trimestres, (2) o time crescer acima de 10 engenheiros ou (3) um módulo específico apresentar requisitos de escala ou blast radius incompatíveis com o monolito.

**Consequências**
- POSITIVO: Time pode focar em entregar features de negócio sem overhead de infraestrutura distribuída nos primeiros meses.
- POSITIVO: Fronteiras de domínio podem ser refatoradas com baixo custo enquanto o negócio ainda está em descoberta.
- POSITIVO: Observabilidade, debugging e rollback são significativamente mais simples num processo único.
- NEGATIVO: Deploy de qualquer módulo requer redeploy do artefato completo — mitigado com feature flags e blue/green deployment no nível de infraestrutura.
- NEGATIVO: Blast radius de uma falha não-tratada afeta todos os módulos — mitigado com módulos isolados, bulkheads internos e tratamento de erros por camada.
- RISCO: Sem governança ativa de fronteiras, o monolito pode degenerar em big ball of mud. Mitigação: code ownership por módulo, testes de arquitetura automatizados (ArchUnit ou equivalente) que impedem dependências cruzadas não-autorizadas.

## Por Que Fronteiras Erradas São Piores que Nenhuma Fronteira

O argumento mais comum a favor de microsserviços desde o início é o de que "é mais fácil começar certo do que refatorar depois". Esse argumento tem uma premissa implícita que raramente é examinada: que você sabe o que é "certo" antes de ter clientes reais.

Na prática, os bounded contexts de uma fintech emergem do uso real. O que parece ser um domínio de "contas" no dia 1 frequentemente se divide em "conta corrente", "conta de pagamento", "conta de investimento" e "limite de crédito rotativo" depois de seis meses de feedback de produto. Se você já construiu quatro microsserviços separados com seus próprios bancos de dados, APIs e pipelines, essa refatoração tem um custo de coordenação enorme: migrações de dados distribuídas, versionamento de APIs, janelas de manutenção coordenadas entre serviços, e risco de inconsistência transacional durante a transição.

Num monolito modular bem estruturado, a mesma refatoração é uma operação de movimentação de código dentro de um repositório, com um único banco de dados para migrar e um único artefato para redesploy. O custo é uma fração.

Isso não significa que microsserviços são errados — significa que eles são uma solução para um problema de escala organizacional e técnica que a maioria das fintechs greenfield ainda não tem. A Lei de Conway é real: sua arquitetura vai refletir a estrutura de comunicação do seu time. Com 5 engenheiros, a estrutura de comunicação ótima é um único time coeso trabalhando num único artefato com fronteiras internas claras. Distribuir artificialmente esse time em "times de serviço" antes de ter escala para isso é uma forma de criar overhead de coordenação sem os benefícios correspondentes.

O padrão *MonolithFirst* de Fowler não é uma recomendação de nunca usar microsserviços. É uma recomendação de ganhar o direito de usá-los: entender seu domínio suficientemente bem para cortar as fronteiras corretas, e ter o time e a maturidade operacional para pagar o custo de operá-los.

## Arquitetura Alvo: Monolito Modular na AWS

Diagrama da arquitetura resultante da decisão: monolito modular rodando em ECS Fargate, com módulos internos explicitamente delimitados, banco de dados único com schemas separados, e pontos de extração futura identificados. O event bus interno (in-process) pode ser externalizado para EventBridge quando o primeiro módulo for extraído.

### 🌐 Edge / Clients

- Mobile App iOS / Android (user)
- Partner API B2B Integrations (external)

### 🔒 Security & Edge — AWS

- AWS WAF + Shield (security)
- API Gateway REST / JWT Auth (edge)
- Amazon Cognito Identity & Tokens (security)

### ⚙️ Compute — ECS Fargate

- Modular Monolith Single deployable artifact (compute)
- Module: Auth ports/adapters (compute)
- Module: Accounts ports/adapters (compute)
- Module: Payments ports/adapters (compute)
- Module: KYC ports/adapters (compute)
- Module: Notifications ports/adapters (compute)
- In-Process Event Bus (→ EventBridge on extraction) (messaging)

### 🗄️ Data — RDS + Cache

- RDS PostgreSQL Multi-AZ (data)
- Schema: auth (storage)
- Schema: accounts (storage)
- Schema: payments (storage)
- Schema: kyc (PCI isolated) (storage)
- ElastiCache Redis Session / Rate limit (data)

### 📊 Observability

- CloudWatch Logs + Metrics (compute)
- AWS X-Ray Tracing (compute)

### 🚀 CI/CD

- CodePipeline + CodeBuild (ci)
- ECR Container Registry (ci)

### 🔗 External Integrations

- Pix / DICT Bacen (external)
- KYC Provider (Serpro / Idwall) (external)

### Fluxos

- mobile -> waf: HTTPS
- partner -> waf: HTTPS
- waf -> apigw
- apigw -> cognito: validar JWT
- apigw -> monolith: rota autenticada
- monolith -> mod_auth
- monolith -> mod_accounts
- monolith -> mod_payments
- monolith -> mod_kyc
- monolith -> mod_notifications
- mod_payments -> eventbus: publica evento
- mod_accounts -> eventbus: publica evento
- eventbus -> mod_notifications: consome evento
- mod_auth -> schema_auth
- mod_accounts -> schema_accounts
- mod_payments -> schema_payments
- mod_kyc -> schema_kyc
- schema_auth -> rds
- schema_accounts -> rds
- schema_payments -> rds
- schema_kyc -> rds
- monolith -> elasticache
- monolith -> cloudwatch
- monolith -> xray
- codepipeline -> ecr: push imagem
- ecr -> monolith: pull & deploy
- mod_payments -> pix: integração Pix
- mod_kyc -> kyc_provider: validação KYC

## Governança de Fronteiras: O Que Separa um Monolito Modular de um Big Ball of Mud

A decisão por monolito modular só tem valor se vier acompanhada de disciplina arquitetural explícita. Sem ela, a velocidade inicial se transforma em dívida estrutural em 12–18 meses, e a extração futura se torna tão custosa quanto teria sido a distribuição prematura.

**Separação de schemas no banco de dados.** Cada módulo acessa exclusivamente seu próprio schema no PostgreSQL. Queries cross-schema são proibidas via code review e, idealmente, via usuário de banco de dados separado por módulo (cada módulo tem um role PostgreSQL com GRANT apenas no seu schema). Isso simula o isolamento de dados que teríamos com bancos separados em microsserviços, sem o custo de gerenciar múltiplas instâncias RDS.

**Interfaces internas formalizadas.** Módulos não chamam diretamente classes ou funções de outros módulos. A comunicação é feita exclusivamente via interfaces públicas definidas na camada de ports (Hexagonal Architecture). Isso garante que, no momento da extração, a interface já existe e pode ser convertida em uma chamada HTTP ou mensagem de evento sem refatoração estrutural.

**Testes de arquitetura automatizados.** Ferramentas como ArchUnit (Java) ou equivalentes em TypeScript (dependency-cruiser) são configuradas no pipeline de CI para falhar o build se uma dependência cross-módulo não-autorizada for introduzida. Essa é a única forma de garantir que a disciplina de fronteiras se mantém conforme o time cresce e a pressão por features aumenta.

**Event bus interno como preparação para extração.** A comunicação assíncrona entre módulos (ex: Payments publica `PaymentCompleted`, Notifications consome e envia o recibo) é feita via um event bus in-process. A interface desse bus é idêntica à que seria usada com EventBridge ou SNS/SQS. Quando o módulo de Notifications for extraído, a única mudança é substituir a implementação do bus por uma chamada real ao EventBridge — o código do produtor e do consumidor permanece inalterado.

**Critérios de extração documentados e revisados.** A decisão de extrair um módulo não deve ser tomada por pressão de momento ou por "já que estamos aqui". Os critérios são definidos agora: bounded context estável por dois trimestres, time acima de 10 engenheiros, ou requisito técnico específico (escala diferenciada, blast radius inaceitável, requisito regulatório de isolamento de rede). Esses critérios são revisados trimestralmente na reunião de arquitetura.

## Comparativo Operacional: Monolito Modular vs Microsserviços — Fase Inicial
| Critério | Dimensão | Monolito Modular | Microsserviços |
| --- | --- | --- | --- |
| Pipelines CI/CD | 1 pipeline | N pipelines (1 por serviço) | — |
| Observabilidade inicial | Logs estruturados + CloudWatch suficiente | Requer tracing distribuído, log correlation, service map desde o dia 1 | — |
| Custo de infra (estimado, fase MVP) | ~$300–600/mês (ECS + RDS Multi-AZ + cache) | ~$1.200–2.500/mês (múltiplos ECS tasks, múltiplos RDS, ALBs, service mesh) | — |
| Refatoração de fronteiras | Baixo custo — move código dentro do repo | Alto custo — migração de dados, versionamento de API, coordenação entre times | — |
| Blast radius de falha | Maior, mas mitigável com bulkheads internos | Menor por serviço, mas falhas de rede introduzem novos modos de falha | — |
| Tempo para primeiro deploy em produção | Semanas | Meses (setup de infra distribuída) | — |

## Roadmap de Evolução Arquitetural

1. **Fase 1 — 0 a 9 meses: MVP com Monolito Modular** — Construir o monolito com módulos explícitos (Auth, Accounts, Payments, KYC, Notifications), schemas separados no PostgreSQL, event bus in-process, e testes de arquitetura no CI. Deploy em ECS Fargate com blue/green. Foco total em validação de produto.

2. **Fase 2 — 9 a 18 meses: Estabilização de Domínio e Crescimento de Time** — Revisar bounded contexts com base em feedback real. Avaliar critérios de extração. Se o time crescer acima de 10 engenheiros ou um módulo apresentar requisito específico, iniciar extração do primeiro candidato (provavelmente Notifications — baixo acoplamento, tolerante a latência, sem requisito de consistência forte).

3. **Fase 3 — 18+ meses: Extração Incremental Baseada em Evidência** — Extrair módulos em microsserviços apenas quando os critérios documentados forem atendidos. Cada extração reutiliza a interface de ports/adapters já existente. O event bus interno é substituído por EventBridge. O schema separado é promovido a banco de dados independente com migração controlada.

## Avaliação pelo AWS Well-Architected Framework

- **security**: Schemas separados por módulo com roles PostgreSQL distintos. WAF + API Gateway com autenticação JWT via Cognito. Dados KYC em schema isolado com criptografia adicional em repouso (RDS encryption + KMS CMK). Secrets gerenciados via AWS Secrets Manager, nunca em variáveis de ambiente ou código.
- **reliability**: RDS Multi-AZ para failover automático. ECS Fargate com health checks e auto-recovery. Blue/green deployment elimina downtime em deploys. Circuit breakers internos por módulo previnem falhas em cascata. Backup automatizado do RDS com retenção de 7 dias.
- **performance**: ElastiCache Redis para sessões e rate limiting (evita round-trips ao banco em paths críticos). Connection pooling no nível da aplicação (PgBouncer ou equivalente). Sem latência de rede entre módulos — chamadas in-process. Escalabilidade horizontal via ECS task scaling quando necessário.
- **sustainability**: Consolidação de carga num único processo reduz consumo de CPU idle comparado a múltiplos containers subutilizados. Fargate Spot pode ser usado para workloads não-críticos (ex: jobs de processamento batch). RDS com instâncias dimensionadas para carga real, não para pico teórico.

> **Minha Perspectiva Senior:** Já vi esse filme muitas vezes. Time de 5 engenheiros, produto ainda não validado, pressão de investidor para "escalar", e a decisão de partir para microsserviços porque "é assim que as grandes fintechs fazem". O resultado, invariavelmente, é seis meses gastos em infraestrutura e zero em produto.

A verdade inconveniente é que microsserviços são uma solução para o problema de escalar times, não de escalar sistemas. Você pode escalar um monolito bem construído para volumes que a maioria das fintechs brasileiras nunca vai atingir. O que você não consegue escalar facilmente é o processo de desenvolvimento quando você tem 50 engenheiros todos tocando no mesmo código. Esse é o problema que microsserviços resolvem — e ele simplesmente não existe com 5 pessoas.

O que eu faria nesse cenário: monolito modular desde o dia 1, com a disciplina de fronteiras sendo não-negociável. Testes de arquitetura no CI que falham o build se alguém importar diretamente de outro módulo. Schemas separados no banco desde o início — isso custa zero e compra muito em termos de preparação para extração futura e isolamento regulatório. Event bus in-process com interface idêntica ao EventBridge.

E o mais importante: documentar os critérios de extração agora, quando não há pressão. Porque quando o time crescer para 12 pessoas e o módulo de Payments estiver com 50 deploys por dia, a pressão para extrair vai ser enorme — e você vai querer ter critérios racionais documentados, não tomar a decisão no calor do momento.

O padrão MonolithFirst do Fowler não é nostalgia. É engenharia de software pragmática aplicad

## Veredicto

Para uma fintech greenfield com time de 4–7 engenheiros e domínio ainda em descoberta, o monolito modular é a decisão arquitetural correta — não por falta de ambição, mas por excesso de pragmatismo.

A escolha não é entre monolito e microsserviços. É entre distribuir complexidade que você entende ou distribuir complexidade que você ainda está aprendendo. A segunda opção cria sistemas que ninguém consegue raciocinar completamente, com fronteiras erradas gravadas em contratos de API e schemas de banco de dados separados.

O monolito modular, construído com disciplina de fronteiras, schemas separados, event bus in-process e testes de arquitetura automatizados, entrega os benefícios essenciais que importam agora — velocidade, simplicidade operacional, refatorabilidade — enquanto preserva o caminho para extração incremental quando as condições reais justificarem.

A decisão de quando extrair é tão importante quanto a decisão de não extrair prematuramente. Documente os critérios. Revise-os trimestralmente. E quando o momento chegar, você terá módulos com interfaces limpas, schemas isolados e um event bus pronto para ser externalizado — não um spaghetti que precisa ser reescrito antes de 

## Referências

- [Martin Fowler — MonolithFirst](https://martinfowler.com/bliki/MonolithFirst.html)
- [Martin Fowler — Microservices](https://martinfowler.com/articles/microservices.html)
- [Martin Fowler — Modular Monolith (Majestic Monolith)](https://martinfowler.com/bliki/MonolithFirst.html)
- [AWS — Strangler Fig Pattern](https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-decomposing-monoliths/strangler-fig.html)
- [AWS — ECS Fargate Getting Started](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/getting-started-fargate.html)
- [AWS — Amazon EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)
- [Sam Newman — Building Microservices (O'Reilly)](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/)
- [dependency-cruiser — Architecture enforcement for JS/TS](https://github.com/sverweij/dependency-cruiser)

## Fontes do caso

- [Martin Fowler — MonolithFirst](https://martinfowler.com/bliki/MonolithFirst.html)
