Aula 05 · 60–75 minutos
Refatore sem inverter o risco
Quinta aula: refatore uma dependência concreta em Go.
Resultado e mapa da aula
Você sairá com um roteiro que reduz risco de refatoração: primeiro preserva a evidência, depois muda uma dependência observável, e só então considera melhorias adjacentes. O compilador e os testes são sensores a cada passo, não uma validação tardia.
- Identifique o import concreto no consumidor.
- Proteja o comportamento com teste.
- Extraia somente a necessidade real.
- Componha o adaptador existente e revise o diff.
Antes da mudança, identifique o acoplamento
Comece pela regra, não pelo repositório. Pergunte: qual ação ela realmente usa? Se o serviço armazena *postgres.OrderRepository, ele conhece o nome e a forma de um detalhe que deveria ficar na borda.
dependência concreta → necessidade real → interface mínima → composição → testeO risco surge ao tentar "abstrair tudo" de uma vez. Uma interface grande ou criada no pacote de infraestrutura apenas move nomes; ela não coloca a política no controle.
Sequência segura de refatoração
- Escreva um teste do comportamento que precisa permanecer.
- Liste os métodos do repositório realmente chamados pelo serviço.
- Declare no consumidor uma interface com só esses métodos.
- Troque o campo e o construtor para o contrato.
- Monte a implementação existente em
maine execute os testes.
A cada passo, o programa ainda deve compilar. Isso reduz a área de diagnóstico quando algo muda.
A mudança mínima
// antes: checkout conhece um detalhe
type Service struct { orders *postgres.OrderRepository }
// depois: checkout possui a necessidade
type OrderSaver interface { Save(context.Context, Order) error }
type Service struct { orders OrderSaver }
Não reescreva o adaptador se ele já tem Save. A implementação implícita de Go faz o trabalho; o ponto importante é remover a importação de infraestrutura do pacote de negócio.
Recupere da memória
De onde deve partir a interface durante a refatoração?
Fonte primária recomendada
Go Code Review Comments — Interfaces. Releia a recomendação de não definir interfaces antes de seu uso real.
Próxima: escolher com critério.
Mude uma seta mantendo o comportamento
Problema em trabalho real: Uma refatoração ampla — criar uma interface global e reescrever todos os repositórios — aumenta a área de mudança e torna difícil atribuir uma regressão. O objetivo é retirar uma dependência concreta de um consumidor específico, não “abstrair o sistema”.
Fluxo e momento de execução: Primeiro, um teste protege o comportamento. Depois, você mede os métodos realmente chamados, declara o contrato no consumidor, altera campo e construtor, conecta o adaptador existente em main e executa compilação/testes a cada passo.
Abordagem insuficiente: Não extraia uma interface Repository com todos os métodos do banco nem mova o contrato para postgres. Isso preserva a infraestrutura como dona da API e cria consumidores acidentalmente acoplados.
Decisão recomendada: Faça a mudança mínima: OrderSaver contém Save, Service o recebe e o repositório atual continua sendo usado se já satisfaz o método. Interfaces diferentes e pequenas podem coexistir para consumidores com necessidades distintas.
Como verificar na prática: Use go test ./... depois de cada passo e revise os imports do pacote checkout: postgres deve desaparecer. Adicione um teste de compilação implícito ao construir o serviço com o adaptador real; a integração continua sendo validada em sua própria suíte.
Limite de segurança: Não aproveite a refatoração para alterar autorização, schema ou tratamento de erro sem testes específicos: isso mistura riscos. Preserve contexto, validação e observabilidade enquanto muda somente a direção da dependência.
Fonte primária: Go Code Review Comments — Interfaces.
Guia de solução do exercício
Fluxo de execução: Proteja o comportamento com teste, descubra os métodos usados, extraia o contrato no consumidor, altere o construtor e componha a implementação existente.
Abordagem inadequada: Trocar todos os repositórios por uma interface enorme em uma alteração única aumenta o risco e apaga a causa de uma falha.
type Repository interface { Save(Order) error; Find(string) (Order, error); List() []Order; Delete(string) error }
Por que falha e como corrigir: A interface representa a capacidade do banco, não a necessidade do serviço. A revisão fica grande e o compilador aponta muitos erros sem explicar a intenção arquitetural.
Abordagem correta: Faça uma mudança mínima guiada por uso e preserve o adaptador atual.
// checkout
type OrderSaver interface { Save(Order) error }
func NewService(orders OrderSaver) *Service { return &Service{orders: orders} }
Raciocínio, trade-offs e limites: Compile e execute os testes a cada passo. Não reescreva PostgresOrderRepository se ele já possui Save. Extraia outro contrato apenas quando outro consumidor tiver outro conjunto de necessidades; interfaces diferentes podem coexistir.
Abra o diagrama relacionado e compare o fluxo.
Pergunta de mercado e entrevista
1. Como refatorar sem aumentar o risco?
Como responder: Use teste de proteção, descubra métodos usados, crie contrato mínimo no consumidor, altere o construtor, componha em main e teste em cada passo.
2. Onde a interface deve nascer numa refatoração?
Como responder: No consumidor que sente o acoplamento. Criá-la no banco costuma exportar detalhes e transforma a capacidade da infraestrutura em uma API geral.
3. Você reescreveria o repositório existente?
Como responder: Não se ele já satisfaz o contrato. A mudança menor preserva comportamento e reduz risco. Reescreva apenas quando o próprio adaptador tiver uma necessidade independente.
Pratique mais 9 questões de recuperação
Responda sem consultar a aula. O feedback é imediato; volte ao trecho relevante antes de tentar de novo.
DI entrega uma dependência; DIP define:
Em Go, uma interface é satisfeita quando:
Quem deve possuir uma interface pequena?
Onde a implementação concreta é escolhida?
Para testar uma regra sem banco, injete:
Uma interface deve conter:
O detalhe PostgreSQL deve depender de:
Qual sinal pede uma interface?
Após a refatoração, a regra não deve importar: