Java 21
Configure como Project SDK do IntelliJ. O terminal da IDE deve enxergar esse mesmo Java.
Você cria apenas o esqueleto no Spring Initializr, abre no IntelliJ e conduz todo o restante pelo agente no terminal da própria IDE. Nada é implementado antes de ser especificado; nada é aceito antes de ser testado e revisado.
Na sala, acompanhe a voz do professor. Animações, jogos e legendas continuam funcionando.
Java 21, IntelliJ, Git, Docker Desktop e um agente CLI autenticado. O roteiro detalhado por sistema operacional continua no Sábado 0.
Configure como Project SDK do IntelliJ. O terminal da IDE deve enxergar esse mesmo Java.
IDE da aula. O plugin Terminal já vem habilitado. Abra com View → Tool Windows → Terminal.
O agente usa o diff como evidência. Nome, e-mail e autenticação do GitHub precisam estar prontos.
Precisa estar aberto. O Postgres será um container reproduzível, sem instalação local.
Escolha Claude Code, Codex CLI ou Google Antigravity CLI. Não precisa instalar os três.
Não instale Maven nem Postgres na máquina. O ZIP do Initializr traz mvnw/mvnw.cmd; o agente usa esse wrapper. O Postgres vem da imagem oficial pelo Docker.
Boa escolha quando a turma segue CLAUDE.md. Instalação nativa para macOS/Linux:
curl -fsSL https://claude.ai/install.sh | bashiniciar: claude
Usa AGENTS.md. Pela instalação npm, tenha Node.js disponível:
npm install -g @openai/codexiniciar: codex
O nome correto é Antigravity. Também reconhece AGENTS.md no workspace:
curl -fsSL https://antigravity.google/cli/install.sh | bashiniciar: agy
AGENTS.md como contrato principal. No Claude Code, um CLAUDE.md curto importa @AGENTS.md. Assim a mesma regra do projeto serve às três ferramentas.br.com.pedidos, artefato e nome pedidos. Pacote: br.com.pedidos.pedidos/ no IntelliJ. Confirme SDK 21 e espere a importação terminar.pom.xml e mvnw. Inicie claude, codex ou agy.Por que o banco entra depois? Com JPA no classpath, o Spring tenta configurar um datasource. Sem URL ou banco embarcado, o teste de contexto pode falhar. Veja a configuração oficial do datasource.
Estamos na raiz do projeto Spring Boot "pedidos" aberto no IntelliJ. Nesta etapa, faça somente um diagnóstico. Não altere arquivos-fonte, configuração ou dependências. Arquivos temporários de build em target/ são esperados. 1. Leia o pom.xml e confirme Java 21, Spring Boot 3.5.16, Web e Validation. Data JPA e PostgreSQL Driver ainda não devem estar presentes. 2. Confirme que o Maven Wrapper existe. Execute os testes pelo wrapper adequado ao sistema operacional. 3. Confirme a versão de Java usada pelo build. 4. Confirme que o Docker responde, sem criar nem iniciar containers. 5. Mostre git status e informe se já estamos num repositório. Entregue uma tabela: verificação, evidência observada e ação necessária. Se algo falhar, pare e explique o bloqueio. Não instale nada por conta própria.
Prova: Teste de contexto do projeto-base verde, Java 21 e Docker respondendo. Nenhuma conexão de banco é necessária.
Se travar: Confira primeiro a versão do Java no terminal. Se já incluiu JPA, compare o pom com a referência; não apague testes nem adicione H2 apenas para esconder o erro.
Baixar referência deste ponto ↗Referências: terminal embutido do IntelliJ · Spring Initializr e Docker · documentação oficial dos três agentes nos cartões acima.
Ninguém nesse sistema errou a sintaxe. Todo teste unitário passava. O erro era de julgamento: alguém decidiu,
provavelmente sem perceber, que mandar um e-mail fazia parte de criar um pedido. E alguém decidiu que o total de um pedido era um
double guardado numa coluna, e que o status era uma String. Decisões pequenas, tomadas por default,
que viraram incidente.
Hoje a IA escreve a sintaxe. Ela também toma esses defaults, e toma exatamente os mesmos, porque aprendeu com milhares de serviços iguais ao do pedido 4711. O que sobra para você é decidir antes de o código existir. É isso que SDD significa na prática: a spec é o lugar onde a decisão é tomada; o código é consequência. E é isso que hexagonal significa na prática: um jeito de organizar o serviço em que "mandar e-mail" e "salvar no Postgres" ficam do lado de fora, atrás de uma porta, e o pedido no centro não sabe que eles existem.
Nesta tarde construímos, do zero, a base para evoluir o serviço do pedido 4711. Isolar as regras ainda não resolve entrega de e-mail ou cobrança duplicada: é o primeiro tijolo, feito do jeito certo. Ele cresce nos sete sábados seguintes até rodar na AWS. Cada decisão que hoje parece cerimônia demais para um CRUD vai se pagar num sábado específico, e o capítulo diz qual.
Esses arquivos não tornam o modelo onisciente. Eles colocam decisões úteis no contexto do agente para que o trabalho comece mais alinhado e possa ser revisado por critérios claros.
O que fazer agora. Aponta para a tarefa, informa o limite desta etapa e pede o próximo resultado: por exemplo, apenas um plano.
Leia a spec 02. Planeje. Não implemente.Como este projeto trabalha. Stack, comandos, arquitetura e convenções que se repetem em várias tarefas.
Java 21 · ./mvnw test · núcleo sem JPAO comportamento desta mudança. Contexto, regras, limites e critérios que depois serão comparados com testes e diff.
pedido vazio → recusar e não salvar.specs/ é uma convenção deste curso. Ela não é carregada automaticamente só por existir. O ritual registrado nas instruções manda o agente abrir a spec relevante antes de planejar.Precisão do exemplo: o Claude Code usa CLAUDE.md como memória de projeto; o Codex descobre AGENTS.md por escopo e hierarquia; o Antigravity CLI reconhece AGENTS.md no workspace. A animação usa o mesmo personagem para representar o fluxo comum.
O teatro mostra um exemplo de revisão com dois itens. Na bancada e na prática Java abaixo, acompanhamos um único item de café com quantidade 2. As vozes ilustram o ritual de trabalho; os critérios executáveis estão nas specs de cada ato.
.specs/ ganha vida [05]Agora acompanhe a construção como se estivesse numa IDE. Repare que cada arquivo nasce para responder uma pergunta: o que é este projeto?, qual mudança queremos?, como saberemos que terminou?
Escolha o lugar que fará a informação aparecer no momento certo sem repetir tudo em cada conversa.
Clique na ordem correta. O objetivo não é decorar comandos: é saber o que precisa existir antes de autorizar uma edição.
Um serviço com um primeiro caso de uso completo, criar pedido, e um segundo que você faz sozinho, adicionar item. Por baixo, a estrutura que vai aguentar o curso inteiro: um domínio que não conhece framework, casos de uso atrás de portas, adapters de entrada (REST) e de saída (JPA para um Postgres num container). A bolinha é uma requisição andando.
Cliente c-1, produto CAFE-500, 2 unidades de R$ 18,90: total R$ 37,80. O programa gera um UUID; “4711” é o nome da história, não o ID enviado à API. Quantidade zero deve ser recusada sem salvar.
Hoje comprovamos regras e isolamento. O incidente de cobrança duplicada e e-mail exige outras decisões, estudadas no bloco de eventos.
☝️ repare na seta tracejada: o teste do caso de uso não precisa de Spring nem de banco. Isso é o hexagonal se pagando pela primeira vez, ainda hoje.
O lab ocupa as quatro horas: abertura, cinco atos de construção e fechamento. Cada passo diz quem está dirigindo:
| Horário | Foco | Tempo |
|---|---|---|
| 13h30 | Abertura e desenho inicial | 15 min |
| 13h45 | Contrato do projeto | 25 min |
| 14h10 | Domínio: decisões, spec e testes | 40 min |
| 14h50 | Intervalo | 15 min |
| 15h05 | Hexagonal e caso de uso | 45 min |
| 15h50 | Banco, persistência e HTTP | 45 min |
| 16h35 | AdicionarItem individual | 25 min |
| 17h00 | Retomada e revisão em dupla | 15 min |
| 17h15 | Fechamento e próxima prática | 15 min |
As pranchetas 2 e 3 já têm um ponto de partida: acrescente apenas a regra ou seta estudada. Complete detalhes em casa. A margem das 17h pode ser usada antes, caso a turma precise.
Entenda o problema → preveja a mudança → leia o plano → autorize → acompanhe a execução → confira a evidência. Os prompts estão junto de cada ato, depois da explicação.
Antes do OK: “Quais arquivos e comportamentos esperamos que mudem?” Depois: “Qual teste ou resultado comprova a regra?”
O professor abre com as dúvidas reais que vocês trouxeram do Cap 0 ("como conferi o que a IA entregou") e conta o 4711. A pergunta que fica no ar até o fim da tarde: em que ponto do sistema cada uma daquelas decisões deveria ter sido tomada?
No Cap 0 você viu que regra dita na conversa cai da mesa. Aqui a consequência prática: o primeiro commit do projeto não é
código, é o contrato. Para a aula funcionar com ferramentas diferentes, o conteúdo principal fica em AGENTS.md.
Codex e Antigravity o reconhecem no workspace; CLAUDE.md importa @AGENTS.md para o Claude Code. Quatro seções
respondem às perguntas que qualquer agente precisa resolver:
./mvnw test, docker compose -f infra/docker-compose.yml up -d.@Entity no domínio..specs/, propor plano, esperar OK, rodar testes antes de dizer pronto, não criar arquivo fora do pedido.A pasta .specs/ começa com um README.md de cinco linhas dizendo o que o serviço é e uma spec por tarefa. No fim do
curso ela tem a história inteira das decisões, datada. É a memória que sobrevive ao contexto, ao colega novo e a você mesmo daqui a
três meses.
Permissões. O agente pode pedir autorização antes de rodar comandos. O aluno lê o comando, o motivo e o alcance.
Testes pelo wrapper, Docker Compose do projeto e git status/diff são esperados neste lab; instalar dependência, apagar arquivo,
acessar fora do projeto ou publicar algo merece interrupção e explicação. O objetivo não é clicar “sim” depressa: é entender o que será executado.
claude, codex ou agy. O projeto já foi diagnosticado pelo Prompt 0. Peça ao agente para inicializar Git se necessário e mostrar o estado, sem commit automático.AGENTS.md, CLAUDE.md e .specs/README.md..specs/, o AGENTS.md e o CLAUDE.md deste site. Repare que são o mesmo formato, num projeto de tamanho real.Vamos preparar as regras do projeto antes do primeiro código. Inspecione o projeto e proponha um plano curto. Depois aguarde meu OK antes de editar. Após aprovação, crie: 1. AGENTS.md como fonte principal, com: objetivo do serviço; Java 21 e Spring Boot; comandos pelo Maven Wrapper; arquitetura hexagonal; domínio e aplicação sem Spring/JPA; dinheiro com BigDecimal; sem Lombok; nenhuma dependência nova sem pedir; ler a spec indicada, planejar, aguardar OK, testar e mostrar diff. 2. CLAUDE.md contendo uma breve apresentação e a importação @AGENTS.md, para o Claude Code receber as mesmas regras. 3. .specs/README.md explicando que cada mudança terá contexto, tarefa, regras e definição de pronto, e que a pasta só entra no contexto quando o agente é instruído a lê-la. Não altere pom.xml nem arquivos Java. Não crie infraestrutura ainda. Não faça commit. Ao terminar, mostre git status --short, diff dos arquivos rastreados e conteúdo dos arquivos novos; relacione cada arquivo ao pedido.
Prova: AGENTS.md, CLAUDE.md e .specs/README.md coerentes. Confira arquivos novos no editor: git diff sozinho não mostra arquivos ainda não rastreados.
Se travar: Peça ao agente para mostrar git status --short e abrir os três arquivos. Só depois autorize o commit do contrato.
# Serviço de Pedidos — regras da casa ## Stack e comandos - Java 21, Spring Boot 3.5.16, Maven Wrapper: `./mvnw` (macOS/Linux) ou `mvnw.cmd` (Windows). - Postgres 16 em Docker: `docker compose -f infra/docker-compose.yml up -d`. Porta 5432, db/user/senha `pedidos`. - Testes: JUnit 5. `./mvnw -q test` antes de dizer que terminou. ## Arquitetura: hexagonal (ports & adapters) - `br.com.pedidos.dominio` records, regras, exceções de domínio. Não importa nada de Spring/JPA/web. - `br.com.pedidos.aplicacao` casos de uso (ports de entrada) e ports de saída (interfaces). Também não importa framework. - `br.com.pedidos.adapters` `entrada.rest` (controllers, DTOs) e `saida.jpa` (entidade JPA, repositório Spring Data, mapper). - `br.com.pedidos.config` classes @Configuration que ligam ports a adapters com @Bean. - Dependências apontam para dentro: adapters conhecem aplicacao e dominio; o inverso nunca. ## Convenções - Domínio imutável: records + List.copyOf. Sem setter. Transição de estado é método que devolve novo objeto. - Dinheiro é BigDecimal. Identidade é String (UUID). - Violação de regra = exceção de domínio nomeada (PedidoFechadoException). Nunca null, nunca boolean de erro. - Sem Lombok. Sem MapStruct. Não adicione dependência ao pom sem me perguntar. ## Como trabalhar - Antes de codar: leia a spec em .specs/ e proponha um plano curto (arquivos + testes). Só execute após meu "ok". - Não crie arquivo que a spec não pede. Sem README, Dockerfile, DTO extra ou "melhorias". - Ao terminar, liste os arquivos tocados e explique cada decisão de design em uma frase.
# Contexto do Claude Code
@AGENTS.mdNos testes de referência, () -> passa uma ação para a asserção. A IDE pode desenhar →; digite - seguido de >. Aqui os operadores ASCII ficam separados.
Antes de pedir classes ao agente, acompanhe o pedido de café. Cada pergunta abaixo precisa de uma decisão verificável.
Não. Quantidade e preço devem ser positivos. A recusa recebe um nome: ItemInvalidoException. Teste zero e negativo.
O total é calculado pelos itens: R$ 37,80. Ele não é recebido como uma segunda verdade. BigDecimal representa os valores decimais.
A mudança escaparia das regras. Um record fixa seus componentes, mas sozinho não protege a lista recebida. List.copyOf cria a cópia defensiva; adicionar item devolve outro Pedido.
Não. StatusPedido nomeia ABERTO, PAGO e CANCELADO. A regra recusa alterações em pedido fechado com PedidoFechadoException.
Preveja a resposta antes de abrir. No teste, procure o mesmo caso. O estado vazio de Pedido.novo é um rascunho transitório; o caso de uso só pode salvar depois de adicionar ao menos um item.
Uma spec tem quatro partes (Cap 0): contexto, tarefa, regras, definição de pronto. O que faz dela uma spec de sênior é que cada linha da seção de regras é um item de checklist de revisão. Para representar essas decisões, escolhemos recursos do Java moderno que um sênior espera ver, e cada uma tem um porquê que o pedido 4711 explica:
record e cópia defensiva. Criou, não muda. Um pedido que passa por uma fila (Bloco 2) ou por duas threads não pode ser alterado por quem tem a referência. List.copyOf no compact constructor fecha a brecha da lista.BigDecimal. double em dinheiro é erro de arredondamento esperando a auditoria.enum hoje, sealed quando houver dados por variante). String aceita "PAGOO". O switch exaustivo garante que um status novo quebra a compilação, não a produção.PedidoFechadoException diz o que aconteceu; IllegalStateException diz que algo aconteceu. Nunca null, nunca boolean de sucesso.pedido.pagar() retorna outro Pedido. Setter não existe.Essas escolhas devem estar na spec: o agente pode sugerir soluções diferentes. Você avalia a proposta pelas regras e evidências, sem presumir que ela estará certa ou errada.
Leia AGENTS.md e .specs/README.md. Não implemente código. Crie somente .specs/01-dominio-pedido.md com contexto, tarefa, regras e definição de pronto. O domínio inicial terá Pedido, ItemPedido e StatusPedido. Use linguagem de comportamento antes de nomes de implementação. Inclua: quantidade e preço positivos; total derivado; dinheiro com BigDecimal; pedido novo começa ABERTO; adicionar item devolve novo pedido; pedido pago ou cancelado não aceita item; listas não podem ser modificadas por fora; nenhuma dependência de Spring/JPA. Pedido.novo cria um rascunho ABERTO e vazio com UUID; adicionarItem devolve outro objeto. ABERTO pode pagar ou cancelar; outras transições são recusadas. Use records e cópia defensiva. Na aula, c-1 compra CAFE-500: 2 x 18.90 = 37.80. Na definição de pronto, transforme cada regra em caso de teste, incluindo erros. Não altere Java, pom.xml ou outros arquivos. Mostre a spec e destaque qualquer ambiguidade que eu precise decidir.
Leia AGENTS.md e .specs/01-dominio-pedido.md. Inspecione a estrutura atual. Primeiro, proponha um plano com a lista exata de arquivos de produção e teste. Para cada arquivo, diga qual regra da spec ele atende. Não implemente até eu responder OK. Após meu OK: implemente somente o domínio e seus testes; execute os testes pelo Maven Wrapper; corrija falhas sem ampliar o escopo; mostre o resultado dos testes, git diff --stat e uma revisão regra por regra da spec. Não use Spring, JPA, Lombok, setters ou double no domínio. Não faça commit.
feat: dominio Pedido.O editor abaixo é um rascunho salvo neste navegador. Use “Começar do esqueleto” para ver a referência; depois copie sua versão para o agente salvar no arquivo indicado. O editor não altera seu repositório. O lint verifica a estrutura do texto, não a correção das regras.
Prova: CAFE-500 × 2 × 18,90 = 37,80; quantidade zero recusada; pedido pago não recebe item; a lista original não altera o pedido. Testes sem Spring.
Se travar: Peça o nome do teste que falhou e compare uma regra por vez. Não aceite remover o teste ou afrouxar a regra para obter verde.
Baixar referência deste ponto ↗Pedido e ItemPedido são record, com List.copyOf no compact constructor.ItemInvalidoException e PedidoFechadoException, não IllegalArgumentException.total() é método, não campo.import org.springframework ou jakarta.persistence em dominio/.Comece sem o desenho. O nosso sistema tem uma parte que decide e partes que traduzem tecnologias. A regra “pedido vazio não pode ser criado” continuaria verdadeira se o Postgres virasse DynamoDB, se REST virasse uma fila ou se o Spring desaparecesse. Portanto, essa regra deve morar numa parte que não depende de nenhuma dessas escolhas.
Pedido, itens, status e casos de uso. Falam a linguagem da empresa e podem ser testados como Java puro.
Contratos que dizem como iniciar um caso de uso ou o que a aplicação precisa pedir ao mundo externo.
Controller REST, JPA, Postgres e, depois, RabbitMQ ou DynamoDB. Traduzem o mundo técnico para os ports.
O Spring que muita gente aprendeu organiza o serviço em três camadas: Controller chama Service que chama
Repository. Essa organização pode funcionar. O problema aparece quando as regras passam a depender de @Entity,
JpaRepository, HTTP ou do contexto do Spring. O teste então precisa subir tecnologias que não fazem parte da decisão de negócio.
Tudo aponta para baixo, e o de baixo é o framework. A regra "só ABERTO paga" mora num if no service, ao lado do save() e do enviarEmail(). É o pedido 4711.
O centro (domínio + aplicação) não importa nada de fora. Quem entra (REST) e quem sai (JPA) são adapters que implementam ou chamam ports: interfaces definidas pelo centro.
interface CriarPedido { Pedido executar(Comando c); }interface AdicionarItem { … }interface Pedidos { Pedido salvar(Pedido p); Optional<Pedido> buscar(String id); }interface PublicadorDeEventos { … }A regra da dependência. Setas de import apontam para dentro. adapters conhece aplicacao e dominio;
aplicacao conhece dominio; dominio não conhece ninguém. Quem liga a interface Pedidos à classe
PedidosJpaAdapter é uma classe de configuração na borda (config/), com @Bean. O núcleo nunca vê a anotação.
O que isso paga, e quando.
CriarPedidoService roda com um Pedidos em memória, em milissegundos, sem Spring. Testes rápidos são testes que rodam.Pedidos ganha um adapter DynamoDB. O domínio e o serviço não sabem que mudaram de banco.O que não fazer. Hexagonal não é criar interface para tudo. Um port existe quando há uma fronteira real
(entrada externa, infraestrutura, algo que você quer trocar ou testar isolado). Pedido não precisa de IPedido. O agente
adora criar interface demais; a spec diz quais existem.
Leia pelas dependências: o caso de uso conhece a interface Pedidos; o adapter JPA também conhece essa interface e a implementa. A interface pertence à necessidade da aplicação, não ao banco.
// Recorte do método executar; classe completa no checkpoint 3. public Pedido executar(Comando comando) { if (comando.itens() == null || comando.itens().isEmpty()) { throw new PedidoSemItensException(); } Pedido novo = Pedido.novo(comando.clienteId()); for (ItemComando item : comando.itens()) { novo = novo.adicionarItem(new ItemPedido( item.sku(), item.quantidade(), item.precoUnitario())); } return pedidos.salvar(novo); } // pedidos é o port Pedidos, recebido pelo construtor.
Pedidos com um HashMap. O mesmo caso de uso roda sem Spring ou Postgres. Isso prova que o adapter é substituível.Base conceitual: artigo original de Alistair Cockburn sobre Ports & Adapters. O hexágono não representa seis camadas; a forma deixa espaço para várias portas ao redor de um dentro protegido.
Atue como tutor de arquitetura para uma turma iniciante em hexagonal. Não altere arquivos. Leia AGENTS.md, a spec 01 e o domínio implementado. Explique usando exatamente este projeto: 1. o que é o núcleo e por que Pedido fica nele; 2. a diferença entre port de entrada, caso de uso, port de saída e adapter; 3. quem chama quem ao criar um pedido; 4. para que lado apontam as dependências de código; 5. como testar CriarPedido sem Spring nem Postgres; 6. um exemplo de interface desnecessária que seria exagero. Use frases curtas, mostre uma árvore de pacotes proposta e termine com cinco perguntas para a turma. Não proponha implementação ainda.
Leia AGENTS.md e .specs/01-dominio-pedido.md. Antes de editar, crie .specs/02-caso-de-uso-criar-pedido.md com o contrato abaixo e mostre seu plano. Aguarde meu OK. O caso de uso recebe cliente e itens, recusa lista vazia, cria o domínio, salva por uma interface chamada Pedidos e devolve o pedido salvo. Crie um port de entrada CriarPedido, sua implementação CriarPedidoService e o port de saída Pedidos. A aplicação não pode importar Spring, JPA, HTTP ou adapters. Após meu OK, implemente e teste com uma implementação em memória de Pedidos dentro do teste. Execute os testes pelo Maven Wrapper. Mostre os imports de aplicacao/, o resultado dos testes e o diff. Não crie Controller, JPA, DTOs, configuração ou commit.
PedidoDTO, PedidoMapper, @Service e qualquer adapter: agora construímos somente o núcleo.aplicacao/? Se há, a fronteira vazou. Depois da correção, peça o commit feat: caso de uso CriarPedido.O editor abaixo é um rascunho salvo neste navegador. Use “Começar do esqueleto” para ver a referência; depois copie sua versão para o agente salvar no arquivo indicado. O editor não altera seu repositório. O lint verifica a estrutura do texto, não a correção das regras.
Prova: CriarPedido recebe itens, devolve 37,80 e salva pelo port. Entrada vazia lança PedidoSemItensException e não chama salvar.
Se travar: Compare os imports e o teste em memória. A implementação do teste é um substituto do banco, não um servidor de banco novo.
Baixar referência deste ponto ↗Use a bancada para prever a tradução em cada fronteira. Depois, escolha a responsabilidade de cada componente no quiz. Essas são as peças que construiremos no Ato 4.
Acompanhe c-1 comprando 2 unidades de CAFE-500 a 18,90. O UUID ilustrativo é o mesmo em todas as etapas; a API gera o seu. Antes de avançar, preveja o que entra, o que sai e qual tecnologia pode aparecer. A bancada é silenciosa para acompanhar a voz do professor.
A pergunta central é sempre: esta peça contém regra, define uma conversa ou traduz uma tecnologia?
dominio no centro, aplicacao em volta, adapters fora. Coloque os ports na borda entre aplicação e adapters. Todas as setas de import apontam para dentro; se alguma apontar para fora, ela está errada e você acabou de encontrar um bug de arquitetura no desenho antes de encontrar no código.Por que Docker. Um Postgres instalado na máquina é um Postgres com versão, porta e senha diferentes em cada
notebook da sala. O container é o mesmo binário, a mesma versão, a mesma configuração, descrito num arquivo versionado junto com
o código. docker compose up -d sobe; down derruba; o volume guarda os dados entre reinícios. No Sáb 3 o
Testcontainers vai subir um desses dentro do teste, e no Sáb 4 o RabbitMQ entra no mesmo arquivo.
A entidade JPA não é o domínio. É a tentação número um, e é o que o agente faz por padrão: colocar @Entity
no Pedido. As entidades JPA deste exemplo precisam de construtor sem argumentos e estado persistível; nossos records representam decisões imutáveis. A solução hexagonal é ter
dois objetos: PedidoJpaEntity (mutável, anotada, mora em adapters/saida/jpa) e o record
Pedido. Um PedidoMapper converte nos dois sentidos. Parece duplicação; é isolamento. Quando a tabela mudar,
o domínio não muda.
O adapter de saída implementa o port Pedidos usando um JpaRepository do Spring Data por dentro. O
adapter de entrada é o controller: recebe JSON num PedidoRequest, monta o Comando, chama o port
CriarPedido, devolve um PedidoResponse. Ele não sabe que existe banco.
Schema. Hoje deixamos o Hibernate criar as tabelas (ddl-auto: update) para a aula andar. No padrão deste curso, produção usa migrações versionadas com Flyway, e entra no Sáb 3 junto com o Testcontainers. Anote como dívida consciente.
spring: datasource: url: jdbc:postgresql://localhost:5432/pedidos username: pedidos password: pedidos jpa: hibernate: ddl-auto: update # só hoje. Sáb 3: Flyway. open-in-view: false # o adapter abre e fecha a sessão; o controller não
Uma fronteira por vez: 4A comprova o banco; 4B comprova o adapter; 4C comprova HTTP. Não avance enquanto a evidência anterior estiver vermelha. Os tempos de 10 + 15 + 20 minutos incluem a explicação no ponto de uso.
Leia AGENTS.md e as specs 01 e 02. Inspecione o ambiente sem editar. Proponha um plano apenas para infra/docker-compose.yml com Postgres 16, serviço postgres, db/user/senha pedidos, porta local configurável e volume nomeado. Verifique conflito na porta 5432. Aguarde meu OK. Após aprovação, crie o compose e suba somente esse serviço. Comprove pg_isready e SELECT 1. Não altere Java, pom.xml ou application.yml. Não remova containers/volumes alheios. Pare se o banco não estiver pronto e explique o erro. Mostre arquivos novos e modificados.
Prova: pg_isready aceita conexões e SELECT 1 retorna 1.
Se travar: Confira Docker aberto, porta ocupada e logs do serviço. Se mudar a porta local, use o mesmo valor no datasource. Nunca apague volumes para “tentar de novo”.
Baixar referência deste ponto ↗Leia AGENTS.md e specs 01/02. Proponha .specs/03-adapters-rest-e-jpa.md com as três provas: banco disponível; persistência pelo adapter; HTTP. Escreva a spec antes do Java. Apresente plano só do checkpoint 4B e aguarde meu OK. Após aprovação, adicione SOMENTE Data JPA e PostgreSQL Driver ao pom. Crie entidades PedidoJpaEntity/ItemJpaEntity, repository Spring Data, mapper manual e PedidosJpaAdapter; application.yml para o Postgres local. Mantenha domínio e aplicação intactos, sem imports de framework. Não crie endpoint ainda. Mapeie a tabela pedido; ID String/UUID do domínio, sem gerar outro ID; total derivado dos itens, sem coluna total. Cada item guarda sku, quantidade e precoUnitario. Use transação no adapter para mapear itens antes de fechar a sessão; open-in-view=false. Preserve os testes unitários. Crie PedidosJpaAdapterIT (integração, execução explícita) e adapte o teste de contexto gerado para essa mesma convenção *IT; não o apague nem desative. Rode os testes unitários pelo Wrapper sem banco; depois execute explicitamente os *IT contra o Postgres: salvar pedido de c-1 com CAFE-500, 2 x 18.90, e recuperar por UUID, em uma nova transação. Verifique itens, status ABERTO e total 37.80. Mostre a consulta à tabela e o diff. Não crie H2, endpoint, Lombok ou MapStruct.
Prova: Teste de integração salva e relê o mesmo UUID em nova transação; itens e total 37,80 conferem.
Se travar: Se a conexão falha, retorne ao 4A. Se os itens somem, confira relacionamento, mapper e limite da transação. Não coloque anotações no domínio.
Baixar referência deste ponto ↗Leia AGENTS.md e specs 01/02/03. Antes de editar, complete na spec o contrato HTTP e proponha o plano. Aguarde meu OK. Implemente POST /pedidos com PedidoRequest, PedidoResponse, PedidoController e config/CasosDeUsoConfig. O controller chama CriarPedido; não conhece JPA. Resposta 201 com UUID, clienteId, itens, status ABERTO e total. Preços e total em JSON são números decimais. Autorize nesta etapa um PedidoExceptionHandler em adapters/entrada/rest: ItemInvalidoException e PedidoSemItensException viram 422 com mensagem; JSON malformado ou campos obrigatórios ausentes viram 400. Use @Valid para formato e presença; a regra quantidade/preço positivos continua no domínio. Não crie outros endpoints. Após implementar, rode unitários sem banco e os *IT com Postgres. Inicie a aplicação e registre status HTTP e corpo para: CAFE-500, quantidade 2, precoUnitario 18.90 -> 201 e total 37.80; quantidade zero -> 422; itens vazios -> 422; JSON malformado -> 400. Nas recusas, comprove que nenhuma linha foi adicionada. Consulte o pedido pelo UUID no banco, reinicie a aplicação e comprove que a mesma linha e seus itens continuam lá. Mostre testes, arquivos novos, diff e encerre somente o processo da aplicação que você iniciou. Não faça commit.
# -i mostra também o status HTTP; o UUID real aparece na resposta. curl -i -X POST localhost:8080/pedidos -H 'Content-Type: application/json' \ -d '{"clienteId":"c-1","itens":[{"sku":"CAFE-500","quantidade":2,"precoUnitario":18.90}]}' # Esperado: HTTP 201, status ABERTO, total numérico 37.80. # 37.8 e 37.80 representam o mesmo valor numérico.
Prova: 201 + UUID + total 37,80. Quantidade zero dá 422 e não grava. O pedido permanece após reiniciar a aplicação.
Se travar: 400: confira o JSON. 422: leia a regra recusada. 500: peça diagnóstico de logs e tradução da exceção, mantendo o núcleo intacto. Depois da revisão, autorize o commit dos adapters.
Baixar referência deste ponto ↗O editor abaixo é um rascunho salvo neste navegador. Use “Começar do esqueleto” para ver a referência; depois copie sua versão para o agente salvar no arquivo indicado. O editor não altera seu repositório. O lint verifica a estrutura do texto, não a correção das regras.
PedidoJpaEntity dentro do adapter, o mapper entre ele e o record, e o curl batendo no controller. Setas sólidas: é tudo síncrono hoje.POST /pedidos/{id}/itens adiciona um item a um pedido aberto. Escreva sua proposta de spec antes de abrir as ajudas. O agente deve aguardar sua revisão da spec e depois do plano.
| Situação | Resultado esperado |
|---|---|
| Pedido com 2 cafés de 18,90; adicionar 1 café de 18,90 | HTTP 200; mesmo UUID; total 56,70 |
| UUID inexistente | HTTP 404; nada salvo |
| Pedido PAGO ou CANCELADO | HTTP 409; pedido preservado |
| Quantidade zero | HTTP 422; pedido preservado |
Teste estados fechados com fixtures nos testes: a aula ainda não tem endpoint de pagamento. Comprove também que CriarPedido continua funcionando.
Quem conhece a regra de adicionar item? Quem busca e salva? Desenhe essas duas responsabilidades antes de escolher arquivos. Na revisão, identifique o que vem do domínio e o que pertence ao caso de uso.
Port AdicionarItem, seu service, exceção PedidoNaoEncontradoException, controller e configuração existentes. Reutilize Pedidos e o mapper. Amplie o handler REST para 404/409/422. O service depende do port, não do JpaRepository.
Agora faremos AdicionarItem de ponta a ponta. Leia AGENTS.md e as specs existentes.
Primeiro, escreva somente .specs/04-caso-de-uso-adicionar-item.md. Inclua: buscar pelo port Pedidos; pedido inexistente gera PedidoNaoEncontradoException; pedido fechado gera HTTP 409; quantidade inválida gera 422; adicionar item atualiza o total; salvar pelo port; endpoint POST /pedidos/{id}/itens devolve 200. Pedido inexistente vira 404. Amplie o handler REST existente, sem mudar CriarPedido. Caso verificável: 37.80 + 1 x 18.90 = 56.70, mesmo UUID; erros não gravam. Liste casos de teste e limites de arquivos. Pare para minha revisão.
Depois do meu OK na spec, proponha o plano e pare novamente. Só implemente após um segundo OK. Ao terminar, execute testes de unidade e o cenário HTTP pelo Maven Wrapper e ferramentas locais, mostre o diff e aponte qualquer import de JPA/Spring dentro de aplicacao/ ou dominio/. Não faça commit.
feat: caso de uso AdicionarItem e, no Caderno, uma decisão aceita com evidência e uma decisão questionada ou corrigida. Quem terminar antes: o GET /pedidos/{id}, com spec.Quem travou: anote a última evidência verde e use a referência desse ponto. Quem concluiu: compartilhe o repositório e comece a revisão em dupla. Se sobrar tempo, aprofunde um caso de erro.
Os ZIPs incluem Wrapper e código até aquele ponto. O primeiro build baixa dependências. São referências para estudo local; não incluem solução de AdicionarItem.
Não altere arquivos. Leia AGENTS.md, a spec da etapa e RETOMADA.md se existir. Identifique o último checkpoint comprovado e a primeira divergência. Mostre o teste/log/arquivo que demonstra o problema. Proponha uma correção mínima, sem remover testes, apagar dados ou adiantar funcionalidades. Aguarde meu OK.
AdicionarItem. Código correto também merece uma justificativa; não é obrigatório inventar defeitos. Use a revisão iniciada no bloco de retomada.Faça uma auditoria final sem alterar arquivos. 1. Leia AGENTS.md e todas as specs. 2. Execute unitários pelo Maven Wrapper e, com o Postgres pronto, execute explicitamente os testes *IT. Diferencie as duas evidências. 3. Liste arquivos modificados e novos por ato. 4. Procure imports de Spring/JPA em dominio/ e aplicacao/. 5. Confira se as entidades JPA estão somente no adapter de saída. 6. Compare cada definição de pronto com uma evidência observável. 7. Mostre git status --short (incluindo novos), diff e riscos ou dívidas conscientes. Nos atos já commitados, compare também os commits correspondentes. Não corrija nem faça commit. Entregue primeiro o relatório para revisão humana. Se aprovamos, eu enviarei comandos separados para os commits.
PagarPedido no projetor.