Ao longo dos últimos anos, discussões relacionadas à arquitetura de estado em aplicações Front-end têm ficado entre as principais pautas da comunidade. Com o amadurecimento dos frameworks disponíveis no mercado e a adoção massiva das SPAs, construir aplicações escaláveis passou a ser um grande desafio.

Em meados de 2014, o Facebook trouxe uma nova proposta com sua arquitetura Flux, fazendo com que a forma como pensamos o Front-end mudasse radicalmente. Nesta proposta, a ideia era deixar para trás a herança trazida pelos primeiros frameworks JS existentes – como Backbone, Ember e o próprio AngularJS – que baseados em frameworks Back-end do começo da década, (Rails e Spring por exemplo) traziam uma abordagem fortemente inspirada em arquiteturas MVC.

Durante o processo de reescrita do Facebook, o time de engenharia notou que a arquitetura de aplicações web usada na época não fazia mais sentido para aplicações grandes e precisava ser repensada.

Imagem1.png

Arquitetura MVC em aplicações front-end robustas

No novo modelo de construção de interfaces baseada em componentes, caso usássemos uma arquitetura MVC tradicional, o controle de estado ficaria espalhado por toda a aplicação, não somente dificultando as mudanças de estado ao longo da árvore de componentes, como também abrindo possibilidade para o surgimento de inconsistências recorrentes.

Problemas relacionados a essa questão, geralmente são colocados em cena quando estamos diante da construção de Front-ends robustos e com UIs complexas. Aqui na Wavy, encaramos esses desafios diariamente durante a construção do ChatClub, nossa plataforma multicanal responsável pelo envio de milhões de mensagens diárias.

Redux FTW

Em 2015, tivemos a chegada do Redux que rapidamente ganhou popularidade e tornou-se a ferramenta de facto para resolver esse tipo de problema. Criado por Dan Abramov, trata-se de uma biblioteca para gerenciar estados de aplicações JavaScript baseada na arquitetura Flux e na linguagem Elm. Apesar de ser agnóstica de frameworks, ganhou enorme adesão na comunidade React desde então.

Os conceitos trazidos pelo Redux são fáceis de serem assimilados e garantem que as mudanças de estado de uma aplicação, que dependa dele, se tornem previsíveis e muito fáceis de serem gerenciadas. São três conceitos básicos que precisamos conhecer:

Store: Uma store é onde o estado da aplicação fica armazenado. Na prática, trata-se de um objeto JavaScript que centraliza toda a árvore de estado da aplicação que antes ficava diluída entre os componentes.

Reducer: Reducers são funções responsáveis por realizar as mudanças de estado. São basicamente funções puras que recebem como parâmetro o estado atual contido na store e a ação responsável por modificá-lo. Sua responsabilidade é recriar uma árvore complementamente nova com as novas mudanças aplicadas.

Action: Uma action descreve ao reducer qual mudança de estado deve ser realizada. Toda vez que a aplicação precisar fazer alguma alteração desse tipo, seja ela em uma view ou outro ponto do código, ela deve recorrer ao disparo de uma ação (dispatch).

Imagem2

Fluxo básico de uma aplicação baseada em Redux

Declarativo x Imperativo

Outra ideia que vem ganhando força nos últimos anos é a programação reativa. A medida em que as aplicações tornam-se complexas, arquiteturas baseadas neste paradigma parecem trazer uma alternativa mais inteligente para lidar com a separação de responsabilidades e com o fluxo de dados de maneira geral.

Com RP (Reactive Programming) podemos escrever um código mais declarativo, ao invés de recorrer a sequências intermináveis de estruturas de controle de fluxo para descrevermos regras de negócio complexas, da maneira como geralmente estamos habituados na programação imperativa.

Hoje temos à disposição uma série de bibliotecas que implementam ferramentas para trabalharmos com programação reativa nas mais diversas linguagens de programação. Aqui em nosso blog, temos um artigo do Moacir Ramos falando um pouco sobre o porquê usamos RxJava em nossas aplicações Android, por exemplo.

No mundo Front-end em especial, temos uma biblioteca chamada RxJS que é uma das mais populares implementações de extensões reativas disponível em JavaScript. Por sorte, o Angular, framework que usamos para a construção da nossa plataforma aqui na Wavy, usa e abusa dessas extensões em vários de seus componentes.

Um dos blocos básicos adicionados à RxJS são os chamados observables,  uma representação de uma série de valores ocorrendo em um determinado intervalo de tempo. São a implementação de um recurso para trabalharmos com streams de eventos, um dos conceitos básicos da programação reativa. Nesse tipo de estrutura podemos representar eventos de clique em um botão ou a mudança em dados de geolocalização, por exemplo.

Imagem3

Uma ilustração de um stream de eventos em programação Reativa

O exemplo abaixo ilustra de maneira simples a diferença entre um código puramente imperativo e outro declarativo. Abaixo, criamos um stream de dados, definimos dois observables que filtram números ímpares e pares separadamente e por fim unimos ambos, criando um só observable para que possamos através de um subscribe recebermos o resultado em um mesmo output.

Captura de Tela 2018-06-18 às 22.23.31

Apesar de interessante, RP é um assunto um pouco extenso e eu não pretendo me aprofundar mais nos seus conceitos nesse artigo. Caso você tenha curiosidade e queria saber mais sobre, é possível encontrar mais informações aqui e nesse outro link também. Vale a pena olhar também esse exemplo bem legal de uma implementação do famoso jogo da cobrinha em JS usando observables e vários operadores dessa biblioteca incrível.

NgRx ou Redux on Steroids

Como usamos Angular para o desenvolvimento do ChatClub aqui na Wavy, o caminho natural para trazermos o Redux para nossa aplicação, foi a adoção do ngrx.

NgRx é um conjunto de bibliotecas criado especificamente para gerenciar estados em aplicações Angular totalmente baseado em RxJS. Fortemente inspirada pelo Redux original, ela compartilha dos mesmos conceitos fundamentais de arquitetura como stores, actions e reducers, e além disso, traz novas capacidades que fazem com que toda mudança de estado seja reativa por padrão. Entre as bibliotecas disponíveis, temos algumas que se destacam:

  • ngrx/store: implementação completa de uma biblioteca para controle de estado em aplicações Angular totalmente Redux-like que utiliza extensões reativas (RxJS) em sua base.
  • ngrx/effects: biblioteca responsável por lidar com “efeitos colaterais” (side-effects) causados por actions que realizam tarefas assíncronas (como requisições http por ex.), isolando o tratamento desses efeitos de funções puras responsáveis somente por cuidar de mudanças de estado.
  • ngrx/router-store: bindings para conectar a ngrx/store com o roteador do Angular (sim, podemos guardar os estados das rotas da aplicação na store também!)
  • ngrx/store-dev-tools: lib que possibilita utilizar a extensão Redux Dev Tools. É uma ferramenta que permite debugar em detalhes as mudanças de estado e que possui outros recursos interessantes, como navegar (voltar ou avançar) no tempo entre as mudanças ocorridas.

Mas, apesar de compartilhar diversas semelhanças com o Redux, temos algumas particularidades aqui. Uma das diferenças existentes, é que aplicações NgRx devem ser estruturadas de maneira que haja uma store por módulo (feature module) Angular, ao invés de somente uma única para toda a aplicação. Somente em tempo de execução é que as stores são agregadas em uma única root store (a famosa single source of truth).

Outra característica interessante é a maneira como os componentes devem ser organizados. Neste tipo de arquitetura, quando desenvolvemos alguma funcionalidade, devemos primeiramente definir um componente pai denominado container que fica responsável por interagir diretamente com a store do módulo ao qual pertence, através do dispatch de actions. Os demais componentes que fazem parte dessa funcionalidade e que estão abaixo dele, os componentes filho, devem somente comunicar-se com o mundo exterior através de inputs e outputs. Sendo assim, o acesso ao estado corrente fica isolado e é passado em pequenos pedaços via inputs.

A grande sacada aqui é que por ngrx ser totalmente baseado em observables, quaisquer mudanças de estados ocorridas, fluem de cima abaixo pela hierarquia de maneira totalmente reativa, fazendo com que os componentes nas camadas inferiores passem por um novo ciclo de renderização em cascata. Estes componentes filho são chamados dumb ou presentational components, pois desconhecem completamente o que acontece no estado externo a eles, o que possibilita maior desacoplamento e consequentemente um maior reaproveitamento de código a longo prazo.

Imagem4

Arquitetura de componentes com NgRx

A abordagem é bastante interessante e facilita a criação de aplicações mais robustas, com maior garantia de consistência em seu estado e que aprendem a reagir à mudança, ao invés de serem explicitamente instruídas a fazerem isso. Outro ganho que temos em consequência dessa novidade, é o fato de podermos alterar a estratégia adotada para detecção de mudanças do Angular (Change Detection) para on push. Com isso, temos uma melhora significativa de performance em processos que envolvem renderização desse componente, desonerando o framework.

O último aspecto interessante que complementa as ferramentas trazidas pelo ngrx, é o uso de effects. Eles servem basicamente para lidarmos com os efeitos colaterais causados pelo dispatch de uma action. Neste contexto, podemos dizer que effects equivalem aos já conhecidos redux-saga ou o redux-thunk do mundo React. O que eles fazem aqui é agir como um middleware, interceptando o disparo de actions e nos possibilitando uma infinidade de opções, como modelar o estado resultante através de operadores RxJS ou até mesmo realizar o disparo de novas actions.

Imagem5

Effects na arquetetura de uma aplicação baseada em ngrx

Temos nesse conjunto de bibliotecas uma ferramenta poderosa que nos ajuda muito no nosso dia a dia e abre caminho para a construção de um Front-end cada vez mais escalável e confiável. A combinação entre gerenciamento de estado à la Redux e o uso de extensões reativas RxJS muda completamente o cenário, revolucionando a maneira como pensamos nossas aplicações atuais. Todo esse poder de fogo aliado a um framework consolidado no mercado, como o Angular e todo o tooling que ele oferece, promovem a construção da web do futuro e nos permitem construir o ChatClub de maneira sólida.

Caso você tenha interesse em se aprofundar pelo assunto e usar o ngrx em suas aplicações, recomendo a leitura deste guia super completo que aborda os conceitos básicos e explica em detalhes o passo a passo para adotá-lo. Há também outras ferramentas de fora do mundo Angular que valem a pena serem estudadas, como o redux-observable, um middleware para baseado em RxJS recomendado pela Netflix ou essas funções de pacote mobx-utils que permitem integrar facilmente o Mobx a RxJS.

Desenvolvedor com sólida experiência nos processos de análise e implementação de aplicações web Entusiasta do software livre, fascinado por novas tecnologias e práticas ágeis.

Posted by:Guilherme Solinscki

<span class="lt-line-clamp__line">Desenvolvedor com sólida experiência nos processos de análise e implementação de aplicações web </span><span class="lt-line-clamp__line lt-line-clamp__line--last">Entusiasta do software livre, fascinado por novas tecnologias e práticas ágeis.</span>

Deixe seu comentário