O Padrão de Projeto Facade no Angular

Andrew Rosário
5 min readMar 14, 2021

--

Este artigo tem como finalidade continuar a difusão do Padrão de Arquitetura em Camadas no Angular. (Veja mais em "Padrões e Boas Práticas em Angular"). Resumidamente esta arquitetura defende que o sistema deve ser dividido em três camadas:

  • Camada Principal: Serviços externos da regra de negócio (Chamadas HTTP, Gerenciamento de Estado e outros serviços)
  • Camada de Abstração: Facade Service que servirá como uma “ponte” entre a camada principal e a camada de apresentação
  • Camada de Apresentação: Lugar onde nossos componentes vivem

Vamos analisar mais a fundo como o padrão de projeto Facade possui um papel importante para a arquitetura da nossa aplicação, abstraindo toda a complexidade dos nossos componentes. Mas antes disso precisamos entender sobre o paradigma reativo.

Reatividade com RxJS

A primeira ruptura a ser feita é a de substituir a tradicional arquitetura Pull-Based pela Push-Based.

Fonte: Push-based Architectures with RxJS

A maioria dos desenvolvedores estão acostumados com a arquitetura Pull-Based, que é um modo imperativo de realizar operações assíncronas. Vamos pensar num exemplo onde a view precisa chamar um serviço para carregar dados do servidor. Em algum momento esses dados precisam ser atualizados, então a primeira reação é chamar novamente a mesma função para o carregamento dos dados.
Não há nada de errado com este método, porém estamos deixando de usar todo o poder que o Angular e o RxJS nos oferece.

Na arquitetura Push-Based temos Observables que emitem dados para as views que estão interessadas (subscribers). Quando eles emitem novos dados, todos os inscritos são notificados automaticamente. Desta forma nós invertemos o paradigma imperativo para o reativo. Não é o desenvolvedor que vai lembrar de atualizar a view quando necessário, mas sim os streams de dados. Eles que possuem a responsabilidade de reagir às mudanças.
Além disso também temos outros benefícios com o Push-Based, como Thomas Burleson mencionou em seu artigo Push-based Architectures with RxJS:

  • Gerenciamento de Estado
  • Imutabilidade
  • Performance
  • Views Reativas

Agora veremos um exemplo de uma simples aplicação com este paradigma aplicado. Imagine uma listagem de notas (ToDos) e um campo de busca. Toda vez que este campo for alterado precisamos realizar novas chamadas no servidor com os dados atualizados.

Para o input de busca utilizamos os poderosos Reactive Forms do Angular. Declaramos um FormControl e então chamamos o valueChanges que retorna um Observable com os valores atualizados.

E aí que entra o conceito reativo do RxJS! Podemos encadear vários operadores em nosso Observable com a função pipe. A parte mais importante está no switchMap, que pegará a busca atualizada e transformá-la em outro Observable. Este nada mais é do que a chamada pro servidor com nossos dados atualizados.

É um código reativo e bastante interessante! Porém temos alguns problemas:

  • Estamos jogando muitas regras para o componente. É uma boa prática deixar nossos componentes limpos para que eles implementem somente regras de visualização.
  • Consequentemente com mais regras o componente fica mais difícil de ser testado e não conseguimos reutilizar este código.
  • Não estamos gerenciando o estado: Uma vez que o componente for destruído perderemos os dados. Uma boa performance em aplicações modernas é essencial e nesses casos precisamos garantir que os dados estejam sempre disponíveis.

Podemos resolver estes problemas com o padrão de projeto Facade. Adicionamos uma camada no meio deste fluxo e desafogamos a responsabilidade do componente. Além disso também podemos armazenar os dados no estado da aplicação.

Observe este detalhe curioso: recebemos como parâmetro da função listenToSearchChanges um Observable do tipo string! Como você já deve ter imaginado, na chamada desta função passaremos o valueChanges do FormControl que virá do nosso componente. Mas o Facade não sabe de onde vem essas mudanças, ele simplesmente sabe que vai receber um Observable e no fim vai atualizar o estado com os dados fornecidos.

Além disso perceba os serviços injetados no construtor:

  • TodosApi: Serviço responsável pelas chamadas HTTP
  • TodosState: Serviço onde está armazenado o nosso estado
  • SearchConfigService: Serviço responsável por gravar e retornar nossa busca realizada no LocalStorage ou em qualquer outro meio de armazenamento

Se voltarmos no começo deste artigo veremos que todos esses serviços são os que foram mencionados na Camada Principal! Agora sim começamos a separar melhor as responsabilidades. É o Facade (Camada de Abstração) que sabe o que fazer, mas são os serviços da camada principal que sabem como fazer tal tarefa. Já o componente (Camada de Apresentação) ficou livre de saber qualquer regra de negócio da aplicação.

Veremos agora como ficou o código do componente:

Muito mais simples, não? Tudo o que o componente precisa fazer é basicamente delegar as ações para o Facade. E agora como estamos gerenciando o estado, os dados do servidor estarão salvos no cache. Logo que o componente for iniciado, veremos imediatamente eles serem renderizados.

Aqui vale somente um adendo: como o estado está sendo atualizado e propagado pelo Facade a partir do subscribe, então precisamos nos desinscrever quando o componente for destruído. Para isso foi utilizado o operador untilDestroyed (Saiba mais em "A forma mais simples de se desinscrever de um Observable no Angular").

Por fim, vamos ver como ficou o resultado final no template HTML do componente:

Mais uma vez transferimos as regras para o Facade. Aqui o componente vai delegar as ações de marcar um ToDo como concluído (facade.SetCompleted) ou de remover um Todo (facade.removeTodo). E o mais importante de tudo: o estado está sendo renderizado em facade.todos$ utilizando o pipe async. Simples, reutilizável e testável!

Vale ressaltar que esta arquitetura não precisa ser seguida estritamente desta forma, são sugestões que quando empregadas da forma correta podem te ajudar muito na criação e manutenção dos seus projetos. Caso tenha interesse, neste repositório do Github são aplicadas as técnicas mencionadas no artigo.

E as Bibliotecas para o Gerenciamento de Estado?

Este padrão acaba por ser bastante semelhante ao uso de uma biblioteca externa de gerenciamento de estado como o NgRx. Dependendo da complexidade da sua aplicação, o uso de Facades atenderá totalmente suas necessidades. Agora se é necessário gerenciar uma grande massa de dados em situações complexas, considere o uso de uma dessas bibliotecas. Você poderá usar também Facades em conjunto com o NgRx, como é explicado neste artigo.

--

--

Andrew Rosário
Andrew Rosário

Written by Andrew Rosário

Desenvolvedor Front-end, mentor e palestrante. Apaixonado por tecnologia e por compartilhar conhecimento.

Responses (1)