Domain-Driven Design (tático) e Arquitetura Hexagonal em uma API de pedidos, com Java 25 e Spring Boot 4.
Lab da Fase 1 da Pós Tech Software Architecture (FIAP): Domain Driven Design, Arquitetura Hexagonal, Qualidade de Software, Desenvolvimento Seguro e Documentação de Arquitetura. O código é original, escrito para praticar os conceitos; não é material do curso. As notas de estudo ficam em pos-tech-software-architecture.
| Conceito | Onde está no código | Nota |
|---|---|---|
| Objeto de Valor | Money, Quantity, OrderId, CustomerId: records imutáveis, iguais por valor, que se validam na criação |
Objeto de Valor |
| Entidade | OrderItem (identidade dentro do pedido) e Product |
Entidade |
| Agregado | Order é a raiz: itens só mudam por ela, e as regras valem sempre |
Agregado |
| Serviço de Domínio | ProductOrderingService: a regra envolve dois agregados (Product e Order) |
Serviço de Domínio |
| Evento de Domínio | OrderCreated, OrderPlaced, OrderCancelled, publicados depois de salvar |
Evento de Domínio |
| Repositório | OrderRepository e ProductRepository são do domínio; a implementação JPA é um adaptador |
Camadas do DDD |
| Portas e Adaptadores | portas de entrada em application.port.in, de saída em application.port.out e nos repositórios; adaptadores em adapter.in.web e adapter.out |
Arquitetura Hexagonal |
| Inversão de Controle | domínio e aplicação não usam Spring; config.UseCaseConfig liga as implementações |
ARQ 05 |
| Contexto Delimitado | um contexto (Vendas); Product ficaria num contexto de Catálogo num sistema real |
Contexto Delimitado |
flowchart LR
client([Cliente HTTP]) --> web
subgraph adapter_in [Adaptador de entrada]
web[OrderController / ProductController]
end
subgraph application [Aplicação]
ports_in{{Portas de entrada<br/>CreateOrder, PlaceOrder...}}
services[OrderApplicationService<br/>CatalogApplicationService]
port_events{{DomainEventPublisher}}
end
subgraph domain [Domínio]
order[Order + OrderItem]
product[Product]
ordering[ProductOrderingService]
repos{{OrderRepository<br/>ProductRepository}}
end
subgraph adapter_out [Adaptadores de saída]
jpa[JPA + PostgreSQL]
events[Eventos do Spring]
end
web --> ports_in --> services
services --> order & product & ordering
services --> repos
services --> port_events
jpa -. implementa .-> repos
events -. implementa .-> port_events
Os diagramas C4 (contexto, contêineres, componentes e implantação) estão em docs/c4. As dependências apontam sempre para o centro. O teste HexagonalArchitectureTest (ArchUnit) quebra o build se o domínio ou a aplicação passarem a depender de Spring, JPA ou de um adaptador.
src/main/java/io/github/geancgbr/orders
├── domain
│ ├── shared Money, Quantity, DomainEvent, DomainException
│ ├── order Order, OrderItem, eventos, OrderRepository, ProductOrderingService
│ └── product Product, ProductRepository
├── application
│ ├── port/in casos de uso
│ ├── port/out DomainEventPublisher
│ └── service OrderApplicationService, CatalogApplicationService
├── adapter
│ ├── in/web REST e tratamento de erros (Problem Details)
│ └── out persistence (JPA) e events (Spring)
└── config ligação das portas com as implementações
- Um pedido nasce como rascunho (
DRAFT), é fechado (PLACED) ou cancelado (CANCELLED). - Só um rascunho aceita itens; no máximo 10 produtos diferentes por pedido.
- Adicionar o mesmo produto de novo soma a quantidade e mantém o preço do primeiro registro.
- Quantidade entre 1 e 99; valores monetários não podem ser negativos.
- Produto inativo não entra no pedido.
- Pedido sem itens não pode ser fechado; cancelamento exige motivo e acontece uma vez.
- Cupom: um por pedido, só em rascunho com itens, de 1% a 50% sobre o subtotal, arredondado em centavos. Cupons válidos:
BEMVINDO10eBLACKFRIDAY25.
Violação de regra vira 422 com a mensagem do domínio; recurso inexistente vira 404.
| Aula | Onde está no projeto |
|---|---|
| QS 01 – Principais conceitos | Caixa branca: OrderAddItemWhiteBoxTest (um teste por caminho do código) e InputValidationWhiteBoxTest, com cobertura de ramos medida pelo JaCoCo; o build exige 90% de ramos cobertos no domínio. Caixa preta: OrderApiBlackBoxTest, só entradas e saídas HTTP, com valores-limite de quantidade (0, 1, 99, 100) |
| QS 02 – Testes de unidade | Testes do domínio em JUnit 5 + AssertJ, isolados e rápidos, no formato Arrange / Act / Assert. A aula usa NUnit, xUnit e MSTest (.NET); @Test equivale a [Test] / [Fact] / [TestMethod] |
| QS 03 – TDD | O cupom de desconto foi feito em três ciclos Red-Green-Refactor. Cada etapa é um commit marcado com [TDD vermelho], [TDD verde] ou [TDD refatoração] no histórico |
| QS 04 – Testes de integração | Ambiente semelhante ao de produção com Testcontainers (PostgreSQL real). Bottom-up: JpaOrderRepositoryAdapterTest testa o adaptador de persistência sozinho; depois a API inteira em OrderApiBlackBoxTest. Os testes de aplicação usam adaptadores falsos em memória (support/InMemoryAdapters) |
| QS 05 – BDD | Cenários em Gherkin em português (src/test/resources/features), executados com Cucumber: especificação viva na linguagem ubíqua. Relatório em target/cucumber/relatorio.html |
| QS 06 – Testes de carga | k6 com o fluxo de compra completo: load-tests/carga.js (uso esperado, p95 < 300 ms e menos de 1% de erros) e load-tests/estresse.js (até 500 usuários virtuais, para achar o ponto de degradação) |
make test # 75 testes + formatação, cobertura e análise estática
make coverage # abre o relatório de cobertura do JaCoCo
make up # sobe a aplicação
make load # teste de carga com k6 (roda em container)
make stress # teste de estresse com k6| Aula | Onde está no projeto |
|---|---|
| DS 01 – Security by Design | Validação de entrada por lista de caracteres permitidos (InputRules) e limites de tamanho. Menor privilégio: a aplicação usa o usuário orders_app, que só lê e grava dados; quem cria e altera tabelas é orders_owner, só nas migrações (db/init/01-roles.sql, conferido por make privileges); o container roda sem root. Segmentação de rede: o PostgreSQL fica numa rede interna, sem porta exposta. Teste de segurança contínuo: SecurityBlackBoxTest e SpotBugs rodam em todo build |
| DS 02 – Buffer overflow, XSS e SQL Injection | SQL Injection: a busca GET /products?name= usa consulta parametrizada (Spring Data); o teste envia ' OR '1'='1, DROP TABLE e UNION SELECT e confirma que viram texto comum. XSS: HTML e script no nome são recusados com 400 sem devolver o conteúdo enviado; a API só responde JSON, com Content-Security-Policy: default-src 'none' e X-Content-Type-Options: nosniff. Buffer overflow: a JVM checa os limites de memória; os campos ainda têm tamanho máximo |
| DS 03 – Bibliotecas de software | SBOM no formato CycloneDX (make sbom, gera target/bom.json). Vulnerabilidades: make deps varre o SBOM com Trivy e falha se houver HIGH ou CRITICAL (resultado: 0). Licenças: make licenses; das 99 dependências, o Trivy aponta como restritas o Logback (LGPL-2.1) e duas APIs Jakarta (GPL-2.0 com classpath exception). As duas têm licença dupla, com EPL como alternativa, e a exceção de classpath permite usar a biblioteca sem que a GPL alcance o projeto |
| DS 04 – Análise estática de código | SpotBugs + FindSecBugs no make test. Achados tratados: CT_CONSTRUCTOR_THROW (corrigido: classes do domínio final), SPRING_CSRF_PROTECTION_DISABLED e EI_EXPOSE_REP2 (aceitos com justificativa, em spotbugs-exclude.xml). SonarQube com make sonar: Quality Gate aprovado, nota A em segurança, confiabilidade e manutenibilidade, 0 bugs, 0 vulnerabilidades, 0 code smells, 93,5% de cobertura |
| DS 05 – OWASP e OWASP Top 10 | DAST com OWASP ZAP sobre a especificação OpenAPI da API (make dast, relatório em target/zap/zap-relatorio.html). O SBOM do make sbom pode ser importado no OWASP Dependency-Track. Mapa do Top 10 abaixo |
| DS 06 – Padrões de segurança | As normas (ISO 27001/27002, NIST 800-53, PCI DSS, CIS) tratam da organização e da infraestrutura. No código, aparecem os controles que elas pedem: controle de acesso por menor privilégio, gestão de vulnerabilidades em componentes e testes de segurança no ciclo de desenvolvimento |
O que o ZAP encontrou na primeira varredura (119 verificações aprovadas, 0 falhas, 2 alertas), todos corrigidos:
| Alerta | Causa | Correção |
|---|---|---|
Erro 500 em GET /products?name=%00 |
O byte nulo chegava ao PostgreSQL, que rejeita o caractere | O parâmetro de busca passou a ser validado com a lista de caracteres permitidos: agora responde 400 |
Erro 500 em GET /products?= |
O Tomcat rejeita parâmetro sem nome e o tratamento genérico devolvia 500 | InvalidParameterException tratada como 400 |
Falta o cabeçalho Cross-Origin-Resource-Policy |
Não estava configurado | Cross-Origin-Resource-Policy: same-origin |
Nenhum teste automatizado tinha pegado os dois erros 500. É o tipo de achado que justifica o teste dinâmico além do estático. Os casos viraram testes em SecurityBlackBoxTest. Na varredura seguinte, o ZAP aprovou as 120 verificações, com 0 alertas.
Por que o CSRF está desligado: a API é stateless, não usa sessão nem cookie, e o CSRF só explora autenticação feita por cookie do navegador. A decisão está registrada no código (@SuppressWarnings("java:S4502")) e no filtro do SpotBugs.
| Categoria | Situação no lab |
|---|---|
| A01 Broken Access Control | |
| A02 Cryptographic Failures | |
| A03 Injection | ✅ Consulta parametrizada, validação de entrada e testes com payloads de SQL Injection e XSS |
| A04 Insecure Design | ✅ Regras de negócio protegidas no agregado (invariantes) e testadas |
| A05 Security Misconfiguration | ✅ Cabeçalhos de segurança, Actuator só com health e info, sem usuário padrão, erros sem stack trace |
| A06 Vulnerable and Outdated Components | ✅ SBOM, Trivy e versões atuais de todas as dependências |
| A07 Identification and Authentication Failures | |
| A08 Software and Data Integrity Failures | ➖ Parcial: o SBOM lista os componentes; não há assinatura de artefatos |
| A09 Security Logging and Monitoring Failures | ✅ Erros inesperados registrados no log sem expor detalhes ao cliente; eventos de domínio registrados |
| A10 Server-Side Request Forgery | ➖ Não se aplica: a API não busca URLs externas |
make privileges # prova o menor privilégio do usuário da aplicação no banco
make deps # vulnerabilidades nas dependências (Trivy)
make licenses # licenças das dependências
make sonar # SonarQube em http://localhost:9000 (admin/admin)
make dast # OWASP ZAP contra a API em execução| Aula | Onde está no projeto |
|---|---|
| DOC 01 – Introdução | Os tipos de documento da aula, todos presentes em docs/: requisitos, HLD, LLD, C4, diagramas de sequência, de classes e de componentes, modelo de dados, documentação de API, manual de instalação, plano de testes, ADRs e DAS. Automação, como a aula recomenda: o contrato da API é gerado pelo springdoc (Swagger/OpenAPI) e os diagramas C4 são gerados por script |
| DOC 02 – HLD e LLD | HLD: visão geral, diagramas de contexto e de contêineres, componentes e requisitos de alto nível, sem detalhe de implementação. LLD: diagrama de classes, máquina de estados do pedido, assinatura de cada porta, algoritmo dos totais, fluxograma do cupom, diagramas de sequência, mapa de erros e mapeamento para o banco |
| DOC 03 – C4 Model | Modelo C4 em Structurizr DSL: um só arquivo gera Contexto, Contêineres, Componentes, a visão dinâmica de aplicar cupom e a de Implantação. O nível de Código, opcional, é o diagrama de classes do LLD. make c4 valida e regenera as imagens; make c4-local abre o modelo no navegador |
| DOC 04 – Requisitos | 10 requisitos funcionais e 11 não funcionais, com prioridade, métrica e rastreabilidade: cada um aponta para o teste ou a ferramenta que o comprova, e os não funcionais trazem o resultado medido (p95, vulnerabilidades, cobertura). O que ficou fora do escopo também está registrado |
| DOC 05 – ADRs | 10 ADRs com contexto, decisão, alternativas e consequências: arquitetura hexagonal, PostgreSQL com Flyway, eventos em processo, cupons em memória, Testcontainers, menor privilégio, CSRF desligado, documentação como código. A ADR-0009 (OAuth2/JWT) está como Proposta, para mostrar o ciclo de vida |
| DOC 06 – DAS | DAS com os 11 elementos da aula: identificação, contexto, requisitos, diagramas, ADRs, plano de testes e monitoramento, modelo de dados (diagrama ER), documentação de API com exemplos reais, instalação por ambiente, continuidade e backup (make backup / make restore, testados) e segurança e compliance (inclui LGPD). Registra também a aprovação e os riscos aceitos |
A documentação também é verificada no build. OpenApiContractTest compara o contrato versionado em docs/api/openapi.json com o que a aplicação publica e falha se houver diferença. Para gerar o contrato com os status certos (201, 204, 400, 404, 422, 500), os endpoints e o tratamento de erros passaram a declarar @ResponseStatus. make docs-check renderiza todos os diagramas Mermaid para pegar erro de sintaxe.
make c4 # valida o modelo C4 e regenera os diagramas
make c4-local # Structurizr em http://localhost:8081
make openapi # atualiza o contrato OpenAPI versionado
make docs-check # confere a sintaxe dos diagramas Mermaid
make backup # pg_dump do banco em backups/
make restore # restaura o backup mais recente (ou FILE=...)Pré-requisitos: Docker e JDK 25.
make up # sobe PostgreSQL e a aplicação em http://localhost:8080
make demo # roteiro completo: catálogo, pedido, regras violadas e eventos
make down # derruba tudo e apaga os dadosContrato completo em docs/api/openapi.json e, com a aplicação de pé, em http://localhost:8080/v3/api-docs.
| Método | Caminho | Corpo |
|---|---|---|
POST |
/products |
{"name": "Teclado", "price": 349.90} |
GET |
/products?name= |
busca opcional por nome |
POST |
/products/{id}/deactivate |
|
POST |
/orders |
{"customerId": "cliente-42"} |
GET |
/orders/{id} |
|
POST |
/orders/{id}/items |
{"productId": "...", "quantity": 2} |
POST |
/orders/{id}/coupon |
{"code": "BEMVINDO10"} |
POST |
/orders/{id}/place |
|
POST |
/orders/{id}/cancel |
{"reason": "cliente desistiu"} |
Java 25 · Spring Boot 4.1 · Spring Data JPA · PostgreSQL 18 · Flyway · JUnit 5 · AssertJ · ArchUnit · Testcontainers · Cucumber · JaCoCo · k6 · Spring Security · SpotBugs + FindSecBugs · SonarQube · OWASP ZAP · CycloneDX · Trivy · springdoc-openapi · Structurizr · Mermaid · Spotless (palantir-java-format) · Docker Compose
