MÓDULO 15 · 3 SEMANAS · ARQUITETURA & DDD

Arquitetura de Software, DDD & Design Patterns

Estilos arquiteturais, Hexagonal, Clean Architecture, DDD tático e estratégico, Strangler Fig, e os padrões GoF que todo engenheiro sênior precisa reconhecer — e saber quando não usar.

Duração ~3 semanas
Conceitos 12 fundamentais
Projeto E-commerce com domínio rico
Pré-requisito Módulos 00, 01, 02
Qualidade Manutenibilidade Legibilidade

Doze conceitos que formam a espinha dorsal das decisões estruturais de um sistema. Da escolha do estilo arquitetural (Layered, Hexagonal, Clean, Event-Driven) ao vocabulário do DDD (Entity, Aggregate, Bounded Context) e aos padrões GoF que descrevem soluções recorrentes. O objetivo não é memorizar — é reconhecer o problema que cada ferramenta resolve e o preço que cobra.

01

Estilos arquiteturais — o mapa antes da escolha

O que é estilo arquitetural vs. padrão vs. decisão tática. Mapa dos estilos principais e os critérios de escolha: acoplamento, mudança, testabilidade, tamanho de equipe.

estudar →
02

Layered Architecture — por que funciona e por que falha

Presentation → Application → Domain → Infrastructure. A regra de dependência, violações comuns (Domain → Infra vazando), e como Layered vira Big Ball of Mud sem disciplina.

estudar →
03

Hexagonal / Ports & Adapters — Cockburn 2005

Domínio no centro; mundo externo como adaptadores intercambiáveis. Ports de entrada (driving) e saída (driven). A razão pela qual testabilidade de domínio deixa de ser luta.

estudar →
04

Clean Architecture — Uncle Bob 2012

Entities → Use Cases → Interface Adapters → Frameworks & Drivers. A Dependency Rule como lei, o anel de Use Cases que Hexagonal não nomeia, e como os dois estilos se relacionam.

estudar →
05

Microservices vs Modular Monolith vs Monolith

O monolito distribuído como anti-padrão. Por que modular monolith é muitas vezes a resposta correta. Os critérios que justificam microservices — e o custo real de operar distribuição.

estudar →
06

Event-Driven Architecture como estilo arquitetural

Os quatro sabores de Martin Fowler: notification, state transfer, event sourcing, CQRS. Choreography vs. orchestration. Versionamento de eventos e testabilidade de fluxos assíncronos.

estudar →
07

Strangler Fig & migração de sistemas legados

Martin Fowler 2004. Anti-corruption layer, roteamento progressivo, dual write, shadow mode. O mapa de riscos e quando a reescrita completa é mais honesta que o estrangulamento.

estudar →
08

DDD Tático I — Entity, Value Object, Aggregate

Eric Evans 2003. Entity como identidade; Value Object como igualdade por valor e imutabilidade; Aggregate como fronteira de consistência com uma raiz. Por que aggregates pequenos são saudáveis.

estudar →
09

DDD Tático II — Repository, Domain Service, Domain Event

Repository como abstração de coleção sobre persistência. Domain Service para lógica que não pertence a entidade. Domain Event para o que aconteceu. Application Service como orquestrador.

estudar →
10

DDD Estratégico — Bounded Context, Context Map, Ubiquitous Language

Bounded Context como fronteira linguística. Ubiquitous Language: mesma palavra, mesmo significado. Context Map e padrões de integração: ACL, Conformist, Customer/Supplier, Shared Kernel.

estudar →
11

Design Patterns I — Creational & Structural

GoF 1994. Creational: Factory Method, Abstract Factory, Builder, Prototype (Singleton e suas armadilhas). Structural: Adapter, Bridge, Composite, Decorator, Facade, Proxy. Quando emergem vs. quando são impostos.

estudar →
12

Design Patterns II — Behavioral

Strategy, Observer, Command, Template Method, Chain of Responsibility, Iterator, Mediator, State. Relação com SOLID e alternativas funcionais. O risco de pattern matching sem contexto de problema.

estudar →
princípio orientador

Arquitetura não é diagrama — é a soma das decisões difíceis de mudar. Um sistema bem arquitetado não é aquele que usa os padrões certos: é aquele cujo custo de mudança é proporcional ao impacto da mudança. DDD e design patterns são ferramentas de vocabulário — eles nomeiam o que os melhores engenheiros faziam antes de ter nome. O senior usa esses nomes para se comunicar, não para impressionar. E sabe, acima de tudo, quando a solução mais simples é a certa.

Estilos arquiteturais e padrões existem para resolver problemas reais — mas cada um carrega custo. As decisões abaixo cobrem os trade-offs que mais aparecem em entrevista e em pull requests reais.

Hexagonal ou Layered?

Layered é a escolha default sensata para equipes menores e sistemas com fronteiras simples — é mais fácil de entender sem onboarding especializado. Hexagonal compensa quando: o sistema tem múltiplos adaptadores de entrada (HTTP, gRPC, fila, CLI) e múltiplos de saída (PostgreSQL, Redis, Kafka, email); quando testabilidade de domínio sem banco é requisito; e quando o time já entende a metáfora. Em prática, sistemas que começam em Layered migram para Hexagonal quando sentem que Infrastructure vaza para Domain. Não adote Hexagonal em sistemas CRUD simples — o overhead de ports e adapters não paga.

Microservices ou Modular Monolith?

Monolito modular é a escolha correta para a maioria dos sistemas novos. Microservices resolvem três problemas específicos: escala de deploy independente (times diferentes, cadências diferentes), escala de carga diferenciada (módulo de vídeo precisa de mais GPU que o módulo de auth), e isolamento de falha entre domínios críticos. Para qualquer coisa fora dessas três condições, o custo operacional de microservices (service mesh, tracing distribuído, deploy complexo, debugging cross-service) supera o benefício. Um monolito com módulos bem delimitados pode virar microservices depois — um monolito distribuído dificilmente volta.

Quando DDD justifica o investimento?

DDD completo — com Aggregates, Domain Events, Ubiquitous Language, Bounded Contexts — paga quando o domínio é genuinamente complexo e muda com frequência: fintech, saúde, e-commerce com regras de negócio ricas. Em CRUD simples, DDD é over-engineering que faz junior demorar mais para entender o código. O sinal prático: se você passa mais tempo modelando regras de negócio do que escrevendo queries, DDD ajuda. Se o sistema é primariamente de leitura e exibição, uma arquitetura mais plana (ou CQRS sem event sourcing) é provavelmente mais honesta.

Strangler Fig ou reescrita completa?

Strangler Fig é a escolha conservadora e muitas vezes correta: risco menor, entrega contínua, base de usuários estável. Mas tem custo real: manter dois sistemas em paralelo por meses, sincronizar dados, testar integração entre novo e velho. Reescrita completa ("big bang") faz sentido quando: o legado é tão acoplado que não tem interfaces para interceptar; o domínio mudou tanto que a lógica existente é irrelevante; ou a dívida técnica é tão severa que cada nova feature no legado custa 5× o que custaria em sistema limpo. A decisão honesta é medir o custo de cada caminho — não romanticizar nenhum dos dois.

Design Patterns: descobrir ou aplicar?

Patterns são descobertos, não aplicados. O caminho saudável: você tem código duplicado ou difícil de estender, extrai a abstração, e então reconhece "isso é um Strategy" ou "isso é um Factory Method". O caminho anti-saudável: você decide que vai usar Strategy antes de ter o problema, e força o padrão em código que seria mais simples sem ele. Padrões GoF nomeiam soluções recorrentes — eles existem para facilitar comunicação ("extrai para Strategy aqui") e para guiar refatoração ("esse if gigante pode virar Chain of Responsibility"). Nunca como objetivo final.

O projeto força a aplicar cada conceito do módulo em um domínio genuinamente complexo — e-commerce com múltiplos bounded contexts, regras de negócio ricas, e necessidade de testabilidade profunda do domínio sem banco de dados.

PROJETO PRÁTICO

E-commerce com domínio rico em Hexagonal + DDD

Sistema de e-commerce com três bounded contexts bem delimitados: Catalog (produtos, variantes, estoque), Orders (carrinho, pedido, pagamento, fulfillment), e Customers (perfil, endereços, crédito). Cada contexto implementado com Hexagonal Architecture e táticas de DDD: Aggregates com invariantes, Domain Events entre contextos, Repositories como abstração de persistência, e Application Services como orquestradores finos.

REQUISITOS
  • Três bounded contexts com linguagem ubíqua documentada (glossário por contexto)
  • Aggregates com invariantes de domínio validadas (Order não pode ser confirmado sem items; estoque não pode ficar negativo)
  • Domain Events publicados a cada transição de estado relevante (OrderPlaced, StockReserved, PaymentApproved)
  • Hexagonal em pelo menos um contexto: ports de entrada (HTTP, fila) e saída (banco, email, payment gateway)
  • Testes de domínio puro — sem banco, sem HTTP — cobrindo todas as invariantes dos aggregates
  • Anti-Corruption Layer entre contextos: Orders não conhece o modelo interno de Catalog
  • Repository pattern com implementação fake para testes e implementação real para produção
  • Ao menos um Design Pattern GoF identificado e nomeado no README com justificativa
  • Context Map documentado: relações entre os três contextos (Customer/Supplier, ACL, etc.)
  • ADR justificando escolha de estilo arquitetural e decisões de DDD não-óbvias
  • README com diagrama C4 L2 dos três contextos e suas fronteiras
Qualidade Manutenibilidade Legibilidade
STACK SUGERIDA POR LINGUAGEM
STACK
.NET 10 + ASP.NET Core Minimal API + MediatR (CQRS leve) + EF Core (Postgres) + MassTransit (RabbitMQ para domain events) + xUnit + FluentAssertions
ESTRUTURA / NOTAS
  • src/Catalog/ — hexagonal: Catalog.Domain/, Catalog.Application/, Catalog.Infrastructure/, Catalog.Api/
  • src/Orders/ — mesma estrutura; Orders.Domain/ com OrderAggregate, OrderLine, invariantes
  • src/Customers/ — Customer aggregate com endereços como Value Objects
  • src/SharedKernel/ — base classes (Entity, AggregateRoot, DomainEvent, ValueObject)
  • tests/*/Domain/ — testes puros de domínio sem mocks de infra
  • MediatR para Application Services (Commands/Queries sem CQRS completo)
  • MassTransit publica Domain Events como mensagens para outros contextos
STACK
Python 3.13 + FastAPI + SQLAlchemy 2 (Postgres) + dataclasses/Pydantic para Value Objects + pytest + pytest-asyncio + RabbitMQ via aio-pika
ESTRUTURA / NOTAS
  • catalog/ — domain/ (entities, vos, aggregates), application/ (services, ports), infrastructure/ (repos, adapters), api/
  • orders/ — order_aggregate.py com invariantes; domain_events.py
  • customers/ — customer aggregate com address como frozen dataclass (VO)
  • shared_kernel/ — base classes Python com dunder __eq__ por valor para VOs
  • tests/ — domínio testado sem SQLAlchemy; fake repositories com lista em memória
  • aio-pika para publicação de Domain Events entre contextos
STACK
Go 1.23 + chi + pgx (Postgres) + NATS ou RabbitMQ para eventos + testify + mockery para fakes de ports
ESTRUTURA / NOTAS
  • internal/catalog/ — domain/ (entities, vos), app/ (use cases), infra/ (postgres repo, http adapter)
  • internal/orders/ — domain/order.go com methods de aggregate; events.go
  • internal/customers/ — Address como struct imutável (VO por convenção)
  • pkg/ddd/ — base types: Entity, AggregateRoot, DomainEvent (interfaces Go)
  • tests em *_test.go com fake repos (structs com map em memória)
  • NATS JetStream para domain events cross-context
entregável

Repositório com: glossário de linguagem ubíqua dos três contextos (mesmo que seja um markdown simples); context map documentado; testes de domínio cobrindo pelo menos cinco invariantes de aggregate; demo de fluxo completo (produto adicionado ao carrinho → pedido criado → pagamento aprovado → estoque decrementado) com Domain Events sendo propagados; e um ADR explicando por que hexagonal foi escolhido e o que seria diferente em layered. O objetivo do projeto não é um e-commerce completo — é provar que você entende as fronteiras.

Arquitetura e DDD são tópicos recorrentes em entrevistas sênior e staff. As perguntas abaixo testam não só conhecimento dos termos, mas a capacidade de articular trade-offs e tomar decisões em contexto.

Q.01

Qual a diferença entre Hexagonal Architecture e Clean Architecture? Em que situação você escolheria um sobre o outro?

Q.02

O que é um Aggregate Root no DDD? Por que aggregates devem ser pequenos? Dê um exemplo de um aggregate mal modelado e como você refatoraria.

Q.03

Você herdou um sistema legado monolítico de 10 anos. O time quer migrar para microservices. Quais perguntas você faria antes de decidir se Strangler Fig é a abordagem certa?

Q.04

Dois times usam a palavra "cliente" mas com significados diferentes: um time fala de quem compra, outro de quem acessa via API. Como DDD estratégico resolve isso? Que padrões de Context Map são relevantes?

Q.05

Você vê um trecho de código com um grande switch/case que instancia objetos diferentes dependendo de um tipo. Que Design Pattern resolve isso? Em que casos você aplicaria e em que casos deixaria o switch/case?