Padrão Estrutural (GoF)

Facade

Fornece uma interface unificada e simplificada para um conjunto de interfaces de um subsistema, tornando o subsistema mais fácil de usar — sem esconder nem vedar o acesso direto ao subsistema quando necessário.

Intenção

Simplificar o acesso a um subsistema complexo expondo uma interface de alto nível que orquestra as interações entre os componentes internos. O cliente usa a Facade para os casos de uso comuns e acessa o subsistema diretamente apenas quando precisa de controle fino.

O Facade pertence à categoria de padrões estruturais no catálogo do GoF (1994). É um dos padrões mais intuitivos — provavelmente você já o aplicou sem saber: sempre que criou uma classe de serviço que orquestra repositórios, validators e notificadores, você criou uma Facade.

Problema

Considere o processo de finalizar uma compra (checkout) em um e-commerce. Este fluxo envolve múltiplos subsistemas: verificação de estoque, processamento de pagamento, cálculo de frete, geração do pedido, envio de e-mail de confirmação e atualização de relatórios. Cada subsistema tem suas próprias classes, dependências e regras.

Sem uma Facade, o código do controller ou do caso de uso precisaria conhecer e orquestrar cada subsistema diretamente — com todas as suas nuances de ordem de chamada e tratamento de erros. Isso cria um acoplamento alto entre o código cliente e os detalhes de implementação dos subsistemas.

Com o crescimento do sistema, qualquer mudança interna em um subsistema (ex.: migrar o gateway de pagamento) exige alterar todos os clientes que o conhecem diretamente.

Facade não é uma barreira impenetrável

Um equívoco comum: a Facade esconde completamente o subsistema, impedindo acesso direto. Não é isso. A Facade simplifica o caso comum — para necessidades avançadas, o subsistema continua acessível diretamente. A Facade é uma porta de entrada conveniente, não um muro.

Solução

O Facade organiza o código em dois grupos principais:

  1. Facade: classe de alto nível que conhece quais classes do subsistema são responsáveis por cada parte do trabalho. Ela delega as solicitações dos clientes aos objetos apropriados do subsistema, gerenciando a ordem de chamadas e ocultando a complexidade de orquestração. Ex.: CheckoutFacade com o método finalizarPedido().
  2. Subsystem classes: as classes que implementam a funcionalidade real. Elas não conhecem a Facade — a Facade é que as conhece. Ex.: ServicoEstoque, ServicoPagamento, ServicoFrete, ServicoEmail.

A Facade pode ter uma ou várias interfaces de entrada — métodos de alto nível que representam os casos de uso mais comuns. Cada método orquestra a sequência correta de chamadas aos subsistemas.

Facade e Singleton

Fachadas são frequentemente implementadas como Singletons — faz sentido ter apenas uma instância da camada de orquestração. Em aplicações com containers de injeção de dependência (NestJS, Laravel, Spring), o container já cuida do ciclo de vida; em aplicações mais simples, é comum ver a Facade como um Singleton explícito ou como um módulo (em Node.js/TypeScript, módulos são naturalmente singletons por cache de importação).

Estrutura

  Código cliente
       │
       │  facade.finalizarPedido(pedido)
       ▼
  CheckoutFacade
  ┌────────────────────────────────────────────────────┐
  │ - estoque: ServicoEstoque                          │
  │ - pagamento: ServicoPagamento                      │
  │ - frete: ServicoFrete                              │
  │ - email: ServicoEmail                              │
  │                                                    │
  │ + finalizarPedido(pedido): ResultadoCheckout       │
  │   1. estoque.verificar(pedido.itens)               │
  │   2. pagamento.processar(pedido.pagamento)         │
  │   3. frete.calcularEAgendar(pedido.endereco)       │
  │   4. email.enviarConfirmacao(pedido.cliente)       │
  └────────────────────────────────────────────────────┘
       │          │          │          │
       ▼          ▼          ▼          ▼
  Subsistemas (independentes, não se conhecem entre si)
  ServicoEstoque  ServicoPagamento  ServicoFrete  ServicoEmail


  Nota: os subsistemas ainda são acessíveis diretamente
  quando necessário — a Facade não os esconde.

Exemplos de código

Exemplo 1 — Facade de checkout de pedido

A Facade orquestra os subsistemas de estoque, pagamento, frete e e-mail numa única chamada de alto nível. O código cliente não precisa conhecer a ordem nem os detalhes de cada subsistema.

// ── Subsistemas — cada um com sua própria responsabilidade ───

class ServicoEstoque {
  verificar(itens: string[]): boolean {
    console.log(`[Estoque] Verificando ${itens.length} item(ns)...`);
    return true; // simulação: sempre disponível
  }

  reservar(itens: string[]): void {
    console.log(`[Estoque] ${itens.length} item(ns) reservado(s).`);
  }
}

class ServicoPagamento {
  processar(valorCentavos: number, metodoPagamento: string): string {
    console.log(`[Pagamento] Processando R$${(valorCentavos / 100).toFixed(2)} via ${metodoPagamento}...`);
    return `TXN-${Date.now()}`; // ID da transação
  }
}

class ServicoFrete {
  calcularEAgendar(cep: string): { prazo: number; valorCentavos: number } {
    console.log(`[Frete] Calculando para CEP ${cep}...`);
    return { prazo: 5, valorCentavos: 1490 }; // 5 dias, R$14,90
  }
}

class ServicoEmail {
  enviarConfirmacao(email: string, transacaoId: string): void {
    console.log(`[Email] Confirmação enviada para ${email}. Transação: ${transacaoId}`);
  }
}

// ── Tipos de domínio ─────────────────────────────────────────

interface DadosPedido {
  itens: string[];
  valorCentavos: number;
  metodoPagamento: string;
  cep: string;
  emailCliente: string;
}

interface ResultadoCheckout {
  transacaoId: string;
  prazoEntregaDias: number;
  freteValorCentavos: number;
}

// ── Facade ───────────────────────────────────────────────────

class CheckoutFacade {
  constructor(
    private readonly estoque: ServicoEstoque,
    private readonly pagamento: ServicoPagamento,
    private readonly frete: ServicoFrete,
    private readonly email: ServicoEmail
  ) {}

  // Interface de alto nível — orquestra toda a sequência:
  finalizarPedido(pedido: DadosPedido): ResultadoCheckout {
    // 1. Verifica disponibilidade
    if (!this.estoque.verificar(pedido.itens)) {
      throw new Error("Um ou mais itens estão indisponíveis.");
    }

    // 2. Processa pagamento
    const transacaoId = this.pagamento.processar(
      pedido.valorCentavos,
      pedido.metodoPagamento
    );

    // 3. Reserva estoque e agenda entrega
    this.estoque.reservar(pedido.itens);
    const entrega = this.frete.calcularEAgendar(pedido.cep);

    // 4. Notifica o cliente
    this.email.enviarConfirmacao(pedido.emailCliente, transacaoId);

    return {
      transacaoId,
      prazoEntregaDias: entrega.prazo,
      freteValorCentavos: entrega.valorCentavos,
    };
  }
}

// ── Uso — o cliente usa apenas a Facade ──────────────────────
const facade = new CheckoutFacade(
  new ServicoEstoque(),
  new ServicoPagamento(),
  new ServicoFrete(),
  new ServicoEmail()
);

const resultado = facade.finalizarPedido({
  itens: ["Livro GoF", "Caderno"],
  valorCentavos: 9800,
  metodoPagamento: "cartao_credito",
  cep: "01310-100",
  emailCliente: "dev@exemplo.com",
});

console.log(`Pedido finalizado! Transação: ${resultado.transacaoId}`);
console.log(`Entrega em ${resultado.prazoEntregaDias} dias.`);
console.log(`Frete: R$${(resultado.freteValorCentavos / 100).toFixed(2)}`);
// [Estoque] Verificando 2 item(ns)...
// [Pagamento] Processando R$98.00 via cartao_credito...
// [Estoque] 2 item(ns) reservado(s).
// [Frete] Calculando para CEP 01310-100...
// [Email] Confirmação enviada para dev@exemplo.com. Transação: TXN-...
// Pedido finalizado! Transação: TXN-...
// Entrega em 5 dias.
// Frete: R$14.90

Quando usar

  • Para simplificar o acesso a um subsistema complexo: quando o código cliente precisa interagir com muitas classes de um subsistema em uma sequência específica, a Facade encapsula essa orquestração.
  • Para criar camadas de abstração entre subsistemas: em arquiteturas em camadas (ex.: Clean Architecture, Hexagonal), a Facade é a fronteira entre a camada de aplicação e os subsistemas de infraestrutura.
  • Para reduzir dependências transitivas: sem Facade, cada cliente do subsistema teria que importar e conhecer todas as classes internas. Com Facade, os clientes importam apenas ela.
  • Para isolar a complexidade de bibliotecas de terceiros: uma Facade sobre uma SDK externa protege o código da aplicação de mudanças na API da biblioteca (semelhante ao Adapter, mas com foco em simplificação de múltiplas chamadas, não em compatibilidade de interface).

Quando evitar

  • Quando o subsistema já é simples: uma Facade sobre um único serviço com um ou dois métodos é burocracia sem benefício.
  • Quando se torna um "God Object": se a Facade acumula lógica de negócio em vez de apenas orquestrar, ela viola o Princípio da Responsabilidade Única. A Facade deve delegar — não decidir.
  • Quando engessa a flexibilidade: se os clientes sempre precisam de controle fino sobre os subsistemas, a camada de abstração da Facade atrapalha mais do que ajuda.

Prós e contras

Prós

  • Simplifica o uso do subsistema para os casos de uso mais comuns.
  • Reduz o acoplamento entre clientes e os detalhes internos do subsistema.
  • Facilita a substituição ou evolução interna do subsistema — os clientes da Facade não precisam mudar.
  • Melhora a legibilidade: o método finalizarPedido() comunica o fluxo de negócio sem expor a mecânica interna.
  • Ponto único para adicionar cross-cutting concerns (logging, métricas, transações) ao fluxo orquestrado.

Contras

  • Pode tornar-se um "God Object" se absorver lógica de negócio além da orquestração.
  • Pode esconder complexidade que o cliente às vezes precisa ver e controlar.
  • Uma Facade que cobre apenas parte do subsistema força o cliente a conhecer tanto a Facade quanto o subsistema diretamente — inconsistência de abstração.

Armadilhas comuns

1. Facade vira God Object com lógica de negócio

A armadilha mais frequente: ao longo do tempo, regras de negócio migram para dentro da Facade ("se o pedido for acima de R$200, frete grátis", "se for cliente premium, aplica desconto"). A Facade passa a acumular responsabilidades que pertencem ao domínio. Mantenha a Facade como orquestradora pura — as regras de negócio pertencem a entidades e serviços de domínio, não à camada de fachada.

2. Confundir Facade com Adapter

O Adapter converte a interface de uma única classe para outra interface esperada pelo cliente. A Facade provê uma interface simplificada para um conjunto de classes. A Facade não precisa que as interfaces internas sejam incompatíveis — ela simplifica, não adapta.

3. Facade que não esconde nada

Atenção: uma Facade que é só um pass-through (apenas repassa chamadas sem nenhuma orquestração ou simplificação) é uma camada de indireção desnecessária. Se o método da Facade é apenas return this.servico.metodo(params), sem adicionar nenhuma coordenação, remova a Facade e acesse o serviço diretamente.

4. Testar a Facade em vez dos subsistemas

Testes unitários devem testar os subsistemas individualmente — não a Facade inteira. A Facade deve ter um teste de integração (ou de orquestração) que verifica a sequência de chamadas com mocks dos subsistemas. Não escreva testes unitários que executam os subsistemas reais pela Facade — isso mistura níveis de teste e torna os testes frágeis.

Padrões relacionados

O Facade interage com outros padrões estruturais e de criação:

O Adapter muda a interface de uma classe para outra; a Facade provê uma interface simplificada para um subsistema — as intenções são distintas, mas ambas criam um ponto de entrada único. O Decorator acrescenta responsabilidades a um objeto mantendo a mesma interface; a Facade pode internamente compor Decorators sobre os subsistemas, mas a Facade em si não precisa manter a interface dos subsistemas. O Singleton é frequentemente combinado com a Facade: faz sentido que exista apenas uma instância da camada de orquestração na aplicação. O Mediator também centraliza a comunicação entre objetos, mas com foco diferente: o Mediator gerencia a interação bidirecional entre componentes que se conhecem mutuamente (reduzindo dependências cruzadas); a Facade gerencia o acesso unidirecional de clientes externos a um subsistema que não conhece o cliente.