A recepcionista do hotel
No hotel você não liga para a governança pedir toalhas, para o restaurante reservar mesa e para a central de táxis marcar corrida — você liga para a recepção, e um balcão só resolve tudo. A recepcionista conhece cada setor, a ordem das coisas e os ramais certos; você conhece um número. E se um dia você quiser negociar direto com o chef, o restaurante continua lá — a recepção é conveniência, não muralha.
O problema
Subsistemas crescem: finalizar um pedido envolve estoque, pagamento, frete e notificação, cada um com sua API e seus pré-requisitos. Sem um ponto de entrada, cada cliente reimplementa essa coreografia.
O resultado é acoplamento espalhado: o checkout web, o app e o job de recompra conhecem as mesmas seis classes — e qualquer mudança interna quebra os três.
A raiz é a falta de uma fronteira: sem facade, a 'interface pública' do subsistema é a soma de todas as suas classes, e a Lei de Deméter morre no cliente — que navega objeto por objeto para montar a operação. O conhecimento sobre COMO o subsistema funciona vaza para quem só queria USÁ-LO.
Não confunda com Adapter: o Adapter TRADUZ uma interface existente para outra que o cliente espera (contratos incompatíveis, geralmente 1-para-1); a Facade SIMPLIFICA — cria uma interface nova, menor e de nível mais alto sobre várias peças (1-para-muitos). Adapter é sobre compatibilidade; Facade é sobre conveniência.
Também não é um Mediator: a facade só conhece o subsistema e o coordena de fora — as peças não sabem dela. No Mediator, os colegas conhecem e conversam através do mediador. E note o parentesco com camadas de serviço: um application service em DDD é, na prática, uma facade sobre o domínio.
Clienteconhece só finalizarPedido()
CheckoutFacadea única porta de entrada
Estoque1. reserva os itens
Pagamento2. cobra
Frete3. agenda a entrega
- 1
Identificar a coreografia
Liste as peças e a ordem que o cliente hoje repete à mão.
- 2
Criar a operação de alto nível
A facade expõe algo como finalizarPedido() com vocabulário de negócio.
- 3
Orquestrar por dentro
Ela chama estoque, pagamento, frete… na ordem certa e traduz erros.
- 4
Cliente fala só com a porta
Uma chamada; o subsistema continua acessível a quem precisa de controle fino.
Navegue pelas camadas
Camada 1/3Cliente
use ← →Camada 1
Cliente
pede a operação de negócio
Chama a facade com a intenção completa ('finalizar pedido com estes itens') e recebe o resultado de alto nível. Não importa, não conhece e não instancia as peças internas.
No código
checkout.finalizarPedido(itens, 150, endereco, email);Se violar: se o cliente importa uma classe interna 'só desta vez', o acoplamento que a facade eliminou volta a se espalhar.
Estrutura
Código
// Subsistema: cada peça tem sua propria API, cheia de detalhes
class Estoque {
reservar(itens: string[]): string { return "res-42"; }
}
class Pagamento {
cobrar(valor: number): string { return "pay-99"; }
}
class Frete {
agendar(endereco: string): Date { return new Date(); }
}
class Notificacao {
confirmar(email: string, previsao: Date): void {}
}
// Facade: uma operacao de alto nivel esconde a coreografia
class CheckoutFacade {
constructor(
private estoque = new Estoque(),
private pagamento = new Pagamento(),
private frete = new Frete(),
private notificacao = new Notificacao(),
) {}
finalizarPedido(itens: string[], valor: number, endereco: string, email: string): void {
const reserva = this.estoque.reservar(itens);
const cobranca = this.pagamento.cobrar(valor);
const previsao = this.frete.agendar(endereco);
this.notificacao.confirmar(email, previsao);
console.log("Pedido ok:", reserva, cobranca);
}
}
// Cliente: uma chamada, zero conhecimento do subsistema
new CheckoutFacade().finalizarPedido(["sku-1"], 150, "Rua A, 10", "ana@ex.com");# Subsistema: cada peca com sua propria API
class Estoque:
def reservar(self, itens): return "res-42"
class Pagamento:
def cobrar(self, valor): return "pay-99"
class Frete:
def agendar(self, endereco): return "2026-08-12"
class Notificacao:
def confirmar(self, email, previsao): pass
# Facade: um ponto de entrada esconde a coreografia
class CheckoutFacade:
def __init__(self):
self._estoque = Estoque()
self._pagamento = Pagamento()
self._frete = Frete()
self._notificacao = Notificacao()
def finalizar_pedido(self, itens, valor, endereco, email):
reserva = self._estoque.reservar(itens)
cobranca = self._pagamento.cobrar(valor)
previsao = self._frete.agendar(endereco)
self._notificacao.confirmar(email, previsao)
print(f"Pedido ok: {reserva} {cobranca}")
# Cliente: uma chamada, zero conhecimento do subsistema
CheckoutFacade().finalizar_pedido(["sku-1"], 150, "Rua A, 10", "ana@ex.com")Casos de uso
SDK de gateway de pagamentos
Cenário
Cobrar um cartão envolve tokenização, análise antifraude, autorização na adquirente e captura — quatro serviços com protocolos próprios que nenhum lojista quer conhecer.
Como o padrão entra
O SDK expõe uma facade: pagamentos.cobrar(cartao, valor). Por dentro, tokeniza, consulta o antifraude, autoriza e captura na ordem certa. A 'developer experience' de gateways como Stripe é essencialmente uma boa facade.
Trade-off: Casos avançados (captura parcial, pré-autorização longa) não cabem na operação simples — o SDK precisa expor também a API granular, senão vira camisa de força.
Processamento de vídeo em streaming
Cenário
Uma plataforma de vídeo converte uploads: demux, decodificação, filtros, re-encoding em múltiplas resoluções, empacotamento HLS — o ffmpeg tem centenas de flags para isso.
Como o padrão entra
Uma VideoFacade expõe transcodificar(arquivo, perfil). Os perfis ('720p-web', '4k-tv') encapsulam as combinações de flags validadas pelo time de mídia; o resto do sistema nunca vê uma flag do ffmpeg.
Trade-off: Cada necessidade nova vira pedido de perfil novo ao time dono da facade — a conveniência centraliza também a fila de manutenção.
Onboarding em banco digital
Cenário
Abrir uma conta exige KYC (documento + selfie), consulta a bureaus, criação da conta no core bancário, emissão de cartão e chave Pix — cinco sistemas, alguns de fornecedores externos.
Como o padrão entra
Uma OnboardingFacade expõe abrirConta(dadosCliente) e coordena os cinco passos, traduzindo erros de cada fornecedor em um resultado único. O app conversa com uma operação; a complexidade regulatória mora atrás do balcão.
Trade-off: Fluxos longos e parcialmente assíncronos (KYC pode levar horas) não cabem numa chamada síncrona — a facade precisa devolver estados intermediários, e a 'interface simples' fica menos simples.
A fachada que vaza o que deveria esconder
Compila, roda, passa no code review. E quebra depois.
A fachada simplifica a chamada, mas devolve — ou aceita — os tipos do subsistema que ela existia para esconder. Quem usa continua acoplado ao que estava atrás; só o caminho ficou mais curto.
import type { PgResult, PgError } from "pg";
import type { S3Object } from "@aws-sdk/client-s3";
class FachadaDePedido {
// Parece simples... e devolve o tipo do driver do banco.
async buscar(id: string): Promise<PgResult> { /* ... */ }
// ... e aceita o tipo do SDK da AWS.
async anexar(id: string, obj: S3Object): Promise<void> { /* ... */ }
// ... e deixa escapar o erro do driver.
async pagar(id: string): Promise<void> {
try { /* ... */ } catch (e) { throw e as PgError; }
}
}
// Quem usa a fachada agora importa 'pg' e o SDK da AWS.
// Trocar Postgres por outro banco continua tocando a aplicacao inteira —
// exatamente o que a fachada prometia evitar.O que quebra
- Ao trocar a dependênciaA migração toca todos os chamadores, porque os tipos do subsistema entraram na assinatura pública.
- No testeSimular a fachada exige construir objetos do driver real, o que é quase tão trabalhoso quanto usar o driver.
- No `package.json`A dependência que deveria ser interna aparece como dependência direta de módulos que nunca deveriam conhecê-la.
A saída: A fronteira da fachada é a assinatura, não a quantidade de linhas economizadas. Tipos de entrada, de saída e de erro precisam ser seus. Se traduzir tudo parecer caro demais, o problema pode ser que ali não cabia uma fachada.
Armadilhas
Facade que vira god object
Começa coordenando, depois valida, depois calcula desconto, depois grava log... Quando a facade acumula regra de negócio, o subsistema vira detalhe e ela vira o monólito interno que todos temem tocar. Facade coordena; regra mora nas peças.
Abstração que vaza
Se finalizarPedido() retorna o objeto ReservaEstoque interno ou lança PagamentoAdquirenteException crua, o cliente acopla nos tipos do subsistema do mesmo jeito — só que agora com uma classe a mais no meio. A facade precisa traduzir entradas, saídas e erros para o seu próprio vocabulário.
Balcão bonito, bagunça atrás
Facade esconde complexidade, não a conserta. Usá-la para 'embrulhar' um módulo caótico e declarar o problema resolvido só adia a dor: a bagunça continua crescendo, agora invisível. Se o subsistema está podre, a facade é o primeiro passo (isolar), não o último (refatorar por trás dela).
Quando usar × quando evitar
Use quando
3- Uma operação de negócio exige coordenar várias classes numa ordem específica, e vários clientes repetem essa coreografia.
- Você quer isolar o resto do sistema de uma biblioteca ou subsistema complexo (um ffmpeg, um SDK verboso).
- Definir a fronteira pública de um módulo: fora dele, só se fala com a facade.
Evite quando
2- O subsistema já é simples — a facade vira só um repasse burocrático de chamadas.
- Os clientes precisam do controle fino que a facade esconde; forçá-los a passar por ela gera métodos com dezenas de parâmetros.
Testar o que ficou
3 armadilhas de Facade 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.
fetchUma função esconde DNS, TLS, connection pooling, redirecionamento e parsing de resposta.Um ORMuser.save()no lugar de montar transação, SQL, binding de parâmetro e tratamento de erro.
O padrão em uma linha
Sem nome de domínio, sem cerimônia. Só a ideia.
// Uma chamada no lugar de seis, com os tipos que sao seus.
await pedidos.confirmar(pedidoId);O que ele cobra
1 salto a mais entre a chamada e o efeito.
- Cada operação nova do subsistema precisa ser exposta de novo
- Recursos legítimos ficam inacessíveis até alguém adicioná-los à fachada
Não vale se: o subsistema já tem uma interface pequena e estável. Fachada sobre fachada só adiciona um salto.