A aduana na fronteira
Na fronteira entre dois países, a aduana traduz documentos, converte moeda e verifica o que entra, para que o de fora chegue ao país adaptado às regras locais. Você não anda pelo país estrangeiro com as regras dele na mala — passa pela aduana, e o que entra já vem no formato de casa. A camada anticorrupção é essa aduana entre dois modelos de software: nada do vocabulário do outro sistema circula do lado de dentro sem antes ser traduzido na fronteira.
A corrupção que a camada evita
O perigo de integrar sem fronteira não é o código ficar feio — é o modelo novo ser contaminado pelo velho. Sem tradução, os conceitos tortos do legado ('TP = 01', o campo que guarda três coisas) vão sendo copiados para o domínio novo, que aos poucos deixa de ter um modelo próprio.
A camada anticorrupção corta isso na borda: ela é o único lugar que conhece o vocabulário do outro. Tudo que atravessa é traduzido para o modelo limpo, e o domínio novo nunca precisa saber que 'TP' existe.
É o Adapter levado de escala: onde o Adapter traduz uma interface (uma classe fala 'dispatch', seu código espera 'enviar'), a camada anticorrupção traduz um modelo inteiro — conceitos, códigos, invariantes — entre dois contextos delimitados. Por isso ela costuma ser mais que uma classe: é um conjunto de tradutores, muitas vezes um módulo próprio, com o nome explícito de fronteira ('legacy', 'acl', 'integration'), para que fique claro que a sujeira mora ali de propósito e não deve vazar.
O ponto sensível é que a tradução tem custo e risco reais. Custo: cada mudança do legado exige atualizar a camada, e ela nunca some (o legado raramente morre). Risco: um mapeamento semântico errado na borda — interpretar 'STA = A' como ativo quando às vezes significa outra coisa — corrompe o modelo novo em silêncio, sem estourar erro. A camada anticorrupção não elimina o problema de ter um legado; ela o confina, trocando corrupção espalhada por uma fronteira concentrada, visível e testável. Quando você controla os dois lados, o certo é alinhar os modelos e apagar a camada — ela existe para fronteiras que você não manda.
Como isso aparece na prática
O ERP cujos códigos mágicos vazaram pelo sistema
Cenário
Um sistema novo consumia um ERP direto, e os códigos dele ('TP', 'STA', 'OBS') foram se espalhando pelo domínio. Quando o ERP mudou o significado de um código, dezenas de pontos do sistema novo quebraram de uma vez.
Como o padrão entra
Uma camada anticorrupção passou a traduzir o modelo do ERP para o modelo limpo na fronteira. O domínio novo passou a receber objetos próprios, sem nunca ver os códigos do ERP.
Trade-off: Surgiu uma camada de tradução a manter, que muda quando o ERP muda. Em troca, uma mudança do ERP passou a impactar um lugar só — a fronteira — em vez de dezenas espalhados.
A migração protegida pela fronteira
Cenário
Durante a substituição gradual de um sistema antigo, o novo precisava conviver com o velho por meses, lendo e escrevendo nos dois. Sem isolamento, o novo corria o risco de nascer já moldado pelo modelo que ia substituir.
Como o padrão entra
Uma camada anticorrupção isolou o modelo novo do antigo durante a transição. O novo evoluiu com seu próprio modelo, e a camada foi encolhendo à medida que o legado era desligado.
Trade-off: A camada é código temporário que exige um dono e um prazo, senão vira fóssil permanente. Em troca, o sistema novo não herdou os vícios do que estava substituindo.
Onde isto dá errado
Deixar o vocabulário do outro vazar
Se um objeto de domínio expõe um campo com o nome ou o código do legado ('tp', 'sta'), a camada não isolou nada — a corrupção passou. O modelo limpo só pode conter conceitos do seu domínio; a tradução tem que ser completa.
Mapeamento semântico errado na borda
Traduzir um código mágico pelo significado errado corrompe o modelo novo sem estourar erro. É o risco mais traiçoeiro da camada: ela concentra a tradução, mas se a tradução está errada, ela concentra o engano.
Manter a camada onde você controla os dois lados
A camada anticorrupção existe para fronteiras que você não manda. Se você controla os dois modelos, o certo é alinhá-los e apagar a camada — mantê-la vira uma indireção que só custa manutenção.
A tradução que confina a sujeira na borda
// O ERP legado fala uma lingua torta: codigos magicos, campos
// reaproveitados, "obs" que guarda tres coisas. Sem defesa, essa
// sujeira vaza para o dominio novo e o contamina inteiro.
// ANTI-CORRUPTION LAYER: uma fronteira que TRADUZ o modelo do outro
// para o seu, e nao deixa o vocabulario dele entrar.
class ClienteACL {
constructor(private erp: ErpLegado) {}
async buscar(id: string): Promise<Cliente> {
const raw = await this.erp.getCli(id); // { TP: "01", STA: "A", OBS: "..." }
return new Cliente({ // traduz para o modelo LIMPO:
tipo: raw.TP === "01" ? "pessoa-fisica" : "pessoa-juridica",
ativo: raw.STA === "A",
// a sujeira do "OBS que guarda tres coisas" e desfeita AQUI, na borda.
observacoes: parseObs(raw.OBS),
});
}
}
// O dominio novo recebe um Cliente limpo e NUNCA ve "TP", "STA", "OBS".
// Toda a sujeira do legado fica confinada nesta camada de fronteira.Quando usar × quando evitar
Use quando
3- Ao integrar com um legado cujo modelo você não controla nem quer imitar.
- Ao consumir uma API de terceiro cujo vocabulário não deve vazar.
- Ao migrar aos poucos, isolando o novo do velho na transição.
Evite quando
2- Quando você controla os dois lados e pode alinhar os modelos.
- Para uma integração trivial e estável, onde a camada custa mais que o acoplamento.
Testar o que ficou
3 armadilhas de Anti-corruption layer viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.
Onde isto aparece de verdade
Você já usa isto, talvez sem saber o nome.
camada sobre um ERP legadoA fina camada que traduz os códigos crípticos e campos reaproveitados de um sistema legado para o modelo limpo do novo.adaptador de API de terceiroIsolar o vocabulário de uma API externa atrás de uma tradução, para que o domínio nunca veja os termos e formatos dela.o pacote 'legacy' isoladoConfinar toda a sujeira de integração num módulo de fronteira, para que ela não vaze para o resto do sistema.
O padrão em uma linha
Sem nome de domínio, sem cerimônia. Só a ideia.
// Traduz o legado no limite — o domínio não vê o DTO velho.
const cliente = acl.paraDominio(legado.obterCliente(id));O que ele cobra
2 saltos a mais entre a chamada e o efeito.
- Uma camada inteira de tradução a manter, que muda sempre que qualquer um dos dois lados muda
- Mapeamento semântico errado na fronteira corrompe o modelo novo em silêncio, sem erro visível
Não vale se: você controla os dois modelos e pode alinhá-los — a camada protege de um sistema que você não controla, não de um que você pode mudar.
Onde isto aparece
Continue por aqui
- AdapterConverte a interface de uma classe em outra que o cliente espera, permitindo que classes incompatíveis trabalhem juntas.
- Arquitetura HexagonalIsola o domínio no centro e o conecta ao mundo externo por portas (interfaces) e adaptadores (implementações), tornando infraestrutura um detalhe plugável.
- RepositoryUma coleção de objetos de domínio que finge não ter banco atrás. O repositório expõe operações na linguagem do negócio — 'pedidos pendentes há mais de uma hora' — e é o único lugar que sabe como isso vira consulta.