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:
- 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!