Desenvolvimento

15 jan, 2019

Arquitetura e desenvolvimento de software – Parte 12: Facade

Publicidade

E aí, pessoal! Como passaram as festas de final de ano?

Voltando à ativa, vamos ao primeiro artigo de 2019. Daremos continuidade à série sobre design patterns. Hoje vamos aprender sobre o pattern Facade.

Facade

Este talvez seja um dos padrões mais conhecidos e utilizados. Seu termo é diretamente relacionado à arquitetura, sendo uma “fachada”, literalmente.

Utilizamos quando queremos reduzir a complexidade de uma operação em um sistema, fornecendo um ponto de responsabilidade única para que um cliente consuma.

Tomaremos como exemplo a criação de uma venda, com itens selecionados com base em um catálogo de produtos de em estoque disponível.

Qual seria o processo para inserir essa venda no sistema? Cadastrar a venda na tabela de vendas e os itens na tabela de itens, cada item com sua chave estrangeira para a venda a qual pertencem. Estes itens são provenientes de um cadastro de produtos, cada um com seu Id.

Já ficou um pouco complicado para o cliente ter que chamar cada uma destas operações, não é verdade? E se tivermos que repetir estes passos em outros setores/módulos do sistema? Como fica a manutenção, mesmo em pequenas modificações?

Com uma Facade, teríamos este processo escrito em um método de “fachada”, chamado IncluirVenda, por exemplo, que executaria este processo de cadastro para nós e retornaria apenas o resultado ou exceção caso alguma parte do processo falhasse.

Ganhamos com isso a possibilidade de testar cada passo individualmente e ainda testar o processo como um todo. Inclusive utilizar injeção de dependência (veremos em um artigo mais a frente) com este pattern facilita muito essa questão de testar nosso código.

Outra vantagem de uma Facade, é que podemos ter implementações diferentes, seguindo uma mesma interface. Imagine que temos uma VendaBalcao e VendaEntrega. O processo é semelhante, com exceção da questão de incluir entrega, postagem, rastreio, frete, transportadora, etc.

Com a criação de uma Interface para a Facade, podemos combinar esse pattern com o Strategy, onde temos processos diferentes, utilizados de acordo com a estratégia do momento, se é uma venda para entrega ou presencial.

Apenas como informação extra, vemos com grande frequência, classes de Serviços em nossas aplicações, sendo implementadas seguindo o modelo de uma Facade para isolar o domínio e regras menores do cliente, e ainda incluindo cada pequeno passo dentro de uma transação gerenciada pelo Service ou Facade. Veremos o diagrama UML na ficha resumo a seguir, assim evitamos ficar muito repetitivos, ok?

Resumindo, Facade é um pattern bem simples, responsável por encapsular sub-sistemas e processos em uma interface simples, facilitando o acesso de clientes a estes processos e reduzindo o acoplamento destes sub-sistemas entre os N clients que podemos ter.

Vamos à ficha resumo do pattern para finalizar.

Ficha Resumo

  • Nome: Facade
  • Objetivo/intenção: simplificar o uso de processos mais complexos e/ou que envolvam vários sub-sistemas em uma interface simples
  • Motivação: quando temos diversos sub-sistemas e/ou processos mais complexos e longos, fazer com que cada cliente precise fazer as devidas chamadas a cada “pedaço” do processo acaba tornando nosso sistema mais complexo de manter. Com o uso de uma interface simples.
  • Aplicabilidade: o padrão Facade é bastante utilizado quando queremos tornar nosso sistema mais fácil de manter, com menor acoplamento e dependência entre os N sistemas que temos com os clients. Um uso muito comum é a criação de Services, utilizando o modelo de um Facade, onde agrupamos o uso de repositories, gateways, etc, através de uma chamada e resposta simples a um método de interface.
  • Estrutura: no desenho abaixo temos um exemplo UML de aplicação do pattern descrevendo o exemplo de venda que mencionamos antes. Detalhando cada parte, temos:
Representação UML do pattern Facade com o exemplo de Vendas que mencionamos.
Diagrama de sequência da Facade acionando cada um dos sub-sistemas, no caso, repositories.
  • Consequências: o pattern Facade auxilia muito isolando a complexidade em operações grandes e/ou que utilizam muitos sub-sistemas ou componentes diferentes de nossa aplicação, mas precisamos tomar muito cuidado, pois o mau uso pode acabar na criação de um “Facade Severino”, que acaba acumulando responsabilidades demais, quebrando o princípio de responsabilidade única.

Em nosso exemplo, a função dele é inserir uma venda, sendo responsável pelos itens e atualização do estoque. A atualização do estoque em si já acaba sendo uma responsabilidade extra, que por boas práticas, deve ser colocada em outra Facade, assim como emitir notas e enviar o pedido por e-mail, cada um para uma Facade diferente, evitando que o objeto torne-se impossível de manter.

Implementações: Abaixo temos um exemplo de código em C# para o pattern:

public class Venda
{
    public string Cliente { get; private set; }
    public IEnumerable<VendaItem> Items { get; private set;}
    
    public Venda(string cliente)
    {
        Cliente = cliente;
        Items = new List<VendaItem>();
    }
    
    public void AdicionarItem(VendaItem item)
    {
        item.Venda = this;
        Items.Add(item);
    }
}

public class VendaItem
{
    public Venda Venda { get; set; }
    public int IdProduto { get; private set; }
    public int Quantidade { get; private set; }
    public decimal Desconto { get; private set; }
    
    public VendaItem(int idProduto, int quantidade, decimal desconto)
    {
        IdProduto = idProduto;
        Quantidade = quantidade;
        Desconto = desconto;
    }
}

public interface IVendaFacade
{
    Venda InserirVenda(Venda venda);
}

public class VendaFacade : IVendaFacade
{

    private readonly IVendaRepository _vendaRepository;
    private readonly IVendaItemRepository _vendaItemRepository;
    
    public VendaFacade(IVendaRepository vendaRepository, IVendaItemREpository vendaItemRepository)
    {
        _vendaRepository = vendaRepository;
        _vendaItemRepository = vendaItemRepository;
    }
    
    public Venda InserirVenda(Venda venda) {
        var vendaCadastrada = _vendaRepository.Inserir(venda);
        
        foreach(var item : venda.Items)
        {
            vendaCadastrada.AdicionarItem(_vendaItemRepository.Inserir(venda, item));
        }
        
        return vendaCadastrada;
    }
}

public class Programa
{
    public static void Main(string[] args)
    {
        /* Fazer o exemplo completo com DI ficaria extenso, então estou apresentando de forma simples
         * Nao facam isso em casa criancas, =) */
        IVendaFacade facade = new VendaFacade(new VendaRepository(), new VendaItemRepository());
        
        Venda venda = new Venda(cliente: "Cliente X");
        venda.AdicionarItem(new VendaItem(idProduto: 1, quantidade: 2, desconto: 10.00));
        venda.AdicionarItem(new VendaItem(idProduto: 5, quantidade: 10, desconto: 25.00));
        
        /* Aqui apenas usamos o método InserirVenda, sem nos preocupar com os N repositorios
         * que o processo precisa, além de podermos reaproveitar isto em vários lugares
         * centralizando a manutenção do processo em um ponto único */
        var vendaCadastrada = facade.InserirVenda(venda);
        
        Console.WriteLine(vendaCadastrada.Id);
    }
}
  • Usos conhecidos: muito utilizado na construção de aplicações com uma quantidade grande de sub-sistemas utilizados em um mesmo processo para abstrair essa complexidade fornecendo interfaces mais simples para que os módulos client possam acessar, reduzindo a complexidade e acoplamento de sistemas, assim como facilitando a manutenção, reuso e testabilidade do sistema.
  • Padrões relacionados: o padrão Abstract Factory pode ser usado com o Facade para fornecer uma interface responsável por criar objetos em um subsistema de forma independente. O Mediator também é muito semelhante ao Facade, pois abstrai a funcionalidade de classes pré-existentes. No entanto, o propósito do Mediator é abstrair comunicação arbitraria entre objetos relacionados, comumente centralizando funcionalidades que não correspondem a nenhum deles. Classes relacionadas ao Mediator não só são cientes deste, como também se comunicam com ele diretamente ao invés de se comunicarem entre si

Concluindo e avisos extras

Este foi o padrão Facade, muito útil quando queremos encapsular processos mais complexos e/ou que utilizam muitos sub-sistemas em uma interface simples, reduzindo o acoplamento destes sub-sistemas com nossos clients e ainda facilitando a manutenção/refatoração de nossos projetos.

Na próxima parte da série abordaremos o último padrão do grupo estrutura, Proxy.

Aproveito para fazer um convite. No próximo sábado, 19, teremos uma edição brasileira do mega evento da Microsoft, o Microsoft Connect(); Brasil, organizado em São Paulo, pela comunidade DevelopersBR, onde vou palestrar sobre ASP.NET Core 2.2 utilizando o recurso de Health Check com Kubernetes.

O Connect(); é um dos mais importantes eventos da Microsoft, que acontece anualmente nos Estados Unidos, trazendo os principais anúncios e novidades quentinhas sobre o Microsoft Azure, diretamente da fonte.

Venha aprender com profissionais referência no mercado sobre essas novidades, fazer muito network e tomar uma cerveja gelada no final do dia conosco.

O evento é gratuito e para se inscrever, basta acessar o link no Meetup:

Quem precisar de certificado, por favor se inscrever também no link do Sympla:

Um abraço a todos e até a próxima!