Onde o teste unitário se encaixa
Arquitetura hexagonal (Ports & Adapters)
Seção intitulada “Arquitetura hexagonal (Ports & Adapters)”A arquitetura hexagonal (também chamada de Ports & Adapters, proposta por Alistair Cockburn) organiza o código em camadas concêntricas, isolando a lógica de negócio de tudo que é externo ao sistema: banco de dados, filas, chamadas HTTP, frameworks.
graph TD
subgraph Fora["Mundo externo"]
Mock["Mock / serviço externo"]
end
subgraph Hexagono["Serviço"]
Adapter["Adapter"]
Controller["Controller"]
Logic["Business Logic"]
Controller --> Logic
Adapter --> Controller
end
Mock -.-> Adapter
- Business Logic: o núcleo. Regras de negócio puras, sem saber nada sobre HTTP, banco ou fila.
- Controllers: orquestram a chamada à lógica de negócio, validam entrada e formatam saída.
- Adapters: fazem a ponte com o mundo de fora (bancos, filas, APIs de terceiros), traduzindo entre o formato externo e o formato que a lógica de negócio entende.
O teste unitário cobre business logic, controllers e adapters, sempre isolando o que está fora do hexágono (bancos reais, filas reais, APIs de terceiros) com mocks ou stubs. Testar a integração de verdade com esses sistemas externos é papel do teste de integração, não do teste unitário.
Estrutura de pastas de um serviço em Ports & Adapters
Seção intitulada “Estrutura de pastas de um serviço em Ports & Adapters”Um jeito comum de organizar um serviço Clojure nesse estilo:
src/service_name/ adapters/ controllers/ db/ diplomat/ logic/ model/ schemata/ components.clj config.clj server.clj service.cljtest/ unit/ integration*/logic/guarda as funções puras com as regras de negócio, o coração do hexágonocontrollers/chamalogic/e decide o que fazer com o resultadoadapters/edb/conversam com banco de dados e outros mecanismos de persistênciadiplomat/é o apelido que o Nubank dá à camada de adapters nesse estilo de arquitetura (a “Diplomat Architecture”), que converte entre o schema externo e o schema interno - veja a nota Diplomat Architecture: o Ports & Adapters do Nubank para os detalhesmodel/eschemata/guardam as estruturas de dados e validações de schema usadas pelo serviçotest/unit/espelha essa mesma estrutura para os testes unitários, enquantotest/integration/guarda os testes que sobem dependências reais (ou próximas do real) para validar a integração ponta a ponta
As pastas marcadas com * (postman*/, integration*/) indicam conteúdo opcional, que nem todo serviço precisa ter.
Por que isso importa: idempotência e reprocessamento seguro
Seção intitulada “Por que isso importa: idempotência e reprocessamento seguro”Um dos ganhos de modelar sua lógica com funções puras e imutabilidade é que fica mais fácil tornar as operações do serviço idempotentes: repetir a mesma operação várias vezes produz o mesmo resultado da primeira vez, sem duplicar efeitos colaterais.
Isso muda a forma de modelar uma operação. Em vez de pensar nela como algo que só produz um efeito e não devolve nada:
f(mensagem) -> voidVocê modela como uma transformação de estado, que recebe o mundo atual e devolve um novo mundo:
f(mensagem, mundo) -> novo-mundoNa prática, isso significa: se uma mensagem de “cobrar R$ 50” for processada duas vezes por causa de um retry de rede, o serviço não cobra R$ 100. Ele reconhece que aquela operação já aconteceu e devolve o mesmo resultado, sem repetir o efeito colateral. Isso facilita bastante lidar com erros e falhas de rede, porque reprocessar deixa de ser arriscado.
A imutabilidade do Clojure ajuda demais nesse jogo: como os valores não mudam por baixo dos seus pés, fica mais simples raciocinar sobre o que aconteceu quando várias operações rodam em paralelo ou concorrentemente, sem efeitos colaterais escondidos disputando o mesmo estado.
Referências
Seção intitulada “Referências”- Ports & Adapters Architecture ou Arquitetura Hexagonal - Augusto Marinho, Bemobi Tech, pt-BR
- Design: Ports and Adapters (Arquitetura Hexagonal) - wsantosdev, pt-BR
- Idempotência em requisições HTTP - Juan Lira, DIO, pt-BR
- Idempotência em APIs REST: o que é e como implementar - Blog Hub do Desenvolvedor, pt-BR