Arquitetura Redux

Rafael Mariano
Engenharia Arquivei
5 min readJun 4, 2019
Photo by Geral on Pixabay

O redux é um gerenciador de estados em javascript. Com ele é fácil testar, desenvolver e depurar aplicações, garantindo flexibilidade para trabalhar com qualquer ferramenta de UI.

Desenvolver aplicações redux pode exigir mais código do que o necessário, mas realmente garante uma aplicação robusta. Um exemplo claro é quando comparado com React hooks, para React, claro.

Uma pré-requisito para esse artigo é entender a arquitetura flux e o redux. Para o redux eu recomendo o curso do Dan Abramov no Egghead.

Você deve encarar uma aplicação redux como uma aplicação com o design orientado a eventos. Para isso, o ideal é isolar o redux das demais camadas do sistema, como UI e chamadas ajax.

Acredito que essas sãos algumas das dúvidas mais comuns daqueles que desconhecem o redux são:

  • Como controlar chamadas no browser, como ajax?
  • O que fazer quando os dados forem organizados de forma errada?
  • Como escalar o redux para problemas ainda mais complexos?
  • Como fazer o sistema resiliente e fácil de modificar?
  • Como escalar aplicações redux? Como usar o reducer em mais de um projeto?

Uma forma de responder essas perguntas é com a criação de uma camada entre a sua UI e o redux . Neste artigo vamos explorar o uso do redux e ao longo do texto responder à essas perguntas.

Arquitetura flux

Controlando a arquitetura

Agora eu mostrarei a visão geral da arquitetura redux que desenvolvi na Arquivei. A ideia central é separar as responsabilidades do redux e definir uma interface de uso.

Cada reducer disponibiliza três interfaces específicas, o despacho das ações (actions), os seletores de dados da máquina de estado (selectors) e o despacho de ações baseadas no estado (creators).

Com esse isolamento, é possível garantir que mudanças no mundo externo (como APIs) não exija modificações nessa interface, e mudanças na UI não exija modificações nas interfaces.

Elementos de uma arquitetura redux

Agora detalharei cada um desses elementos lógicos da arquitetura, dando alguns exemplos de implementação.

Store

O store é a abstração dos dados. A sua organização tradicional é uma árvore complexa e estruturada que representa detalhadamente o estado da aplicação. Esta árvore deve ser obrigatoriamente imutável pois desta forma não haverá a possibilidade de gerar efeitos colaterais na manipulação dos estados.

Simples não!? Bom, pra deixar essa esse controle mais completo e legível eu vou usar o ImmutableJS em todos os meus exemplos. É uma biblioteca muito simples que garante a imutabilidade de forma harmoniosa. Como a sua leitura é simples, tornará os exemplos deste artigo mais objetivos.

Reducers

O reducer é a descrição das transições de estados. Até aqui nada de especial.

Dentro dessa arquitetura este é o único dispositivo que tem a capacidade de modificar os dados da aplicação. O scaffold serve como base para terceiros definirem um estado inicial.

Selectors

Este é o dispositivo que expõe o dado para fora do reducer com uma interface bem definida. Com esse dispositivo é possível garantir que nada precise ser modificado no caso de uma alteração nos dados do reducer .

O state é a descrição instantânea completa da máquina de estados. O selector é a exposição de um dos dados presente nessa descrição instantânea.

Actions

Este dispositivo é a interface que gera a intenção de alteração de um dado, ou seja, ele descreve a intenção de uma operação da máquina de estados. Ele é o único elemento que conhece o reducer.

Creators

Este dispositivo é o único capaz de se comunicar com os efeitos colaterais (effects), além de conhecer os dados através dos seletores (selectors) e fazer transições através das intenções (actions).

Para essa camada funcionar é necessario o uso do midddleware redux-thunk.

Effects

É o único dispositivo capaz de conversar com os diferentes gateways da sua aplicação como API, extensões, cookies, location, storage, serviços de terceiros e chamadas de protocolos do sistema.

Escalando a aplicação

Cada reducer deve representar um recurso específico da sua aplicação. Com isso é possível integrar facilmente uma aplicação redux com uma REST API.

Como toda aplicação possui mais de um recurso para gerenciar é de se esperar que a arquitetura redux use reducers combinados. Ao combinar reducers é necessário modificar os selectors para capturar o reducer que representa o recurso e depois selecionar o dado. Essa modificação é realmente simples e deixarei uma aplicação com esse exemplo.

O mundo real

No mundo real algumas dificuldades podem aparecer sobre essa arquitetura. Vou relatar algumas que enfrentei mas que não afetaram o funcionamento da arquitetura.

Além disso, criei um exemplo mais próximo do mundo real sobre essa arquitetura: https://github.com/toruticas/redux-architecture.

Nem tudo é RESTful API

Nem toda aplicação é RESTful API. Logo nem todo reducer vai representar um recurso. Está tudo bem, não tem problema criar um reducer mais genérico. Na Arquivei nem tudo é API, o que dirá RESTful. Nós combinamos uma série de recursos em um mesmo reducer. Algumas vezes isso é elegante, outrora é completamente caótico (maioria dos casos).

Effects

Nem sempre usamos creators. Na verdade, quase nunca. O mais comum na nossa aplicação é chamarmos tudo de effects, ainda que não haja controle de um efeito colateral. Com isso a nossa aplicação fica mais simples e evita uma implementação desnecessária.

Testes unitários e de integração

Com esse isolamento fica muito mais fácil criar testes unitários. Os reducers, actions, selectors já estão isolados por natureza. No caso dos effects nós mockamos todos os recursos externos pois o objetivo é testar exclusivamente o código.

Recomendo fazer um teste de integração para os effects sem mockar os dados com o auxílio de uma biblioteca que mocka especificamente a ferramenta que você está usando.

Em resumo, a axios seria mockada nos testes unitários através do “jest.mock”. Nos testes de integração é ideal usar o axios-mock-adapter.

ImmutableJS

Algumas vezes é necessário passar um elemento Map do store para um componente React. Para isso é necessário extrair cada dado dentro do React usando get (Immutable). Eu prefiro sempre encarar desse jeito. Sempre trabalhar com a estrutura de dados do Immutable e não com os tipos primitivos do javascript. Em resumo usar List e Map invés de Array e Object.

Caso você julgue necessário usar o objeto nativo javascript você pode usar o método toJS e garantir que não terá problemas de performance com o reselect, uma vez que todos Connect é tratado como um PureComponent por default.

A ideia de criar uma máquina de estados para controlar uma aplicação inteira é realmente mágico pois você sempre saberá onde estão os seus dados e como manipulá-los. O react-redux permite deixar o dados o mais próximo possível do componente, isso é realmente lindo.

Depois de um tempo trabalhando com essa arquitetura fica muito mais fácil trabalhar com TDD. Trabalhar da forma contrária chega a ser esquisito.

Usem redux!

--

--