Escolar Documentos
Profissional Documentos
Cultura Documentos
Padrões de arquitetura
VISÃO GERAL
Definição dos padrões de arquitetura usados no desenvolvimento front-end utilizando o novo
padrão de camadas.
OBJETIVOS
1. Abordar quais camadas existem no projeto e suas responsabilidades;
2. Documentar convenções e padrões a serem seguidos no projeto.
CLEAN ARCHITECTURE
Como já mencionado, a Clean Architecture foi desenvolvida para outra realidade, porém,
pode ser adaptada ao front end, claro, alterando um pouco alguns conceitos para se adaptar
a essa realidade.
Aqui ficam as views, essa camada é responsável por criar as partes visuais que aplicação
vá ter, juntando components para criar layouts compostos e com a complexidade necessária
para atender ao que o negócio necessita, devemos manter o mínimo possível de regras
aqui, idealmente apenas verificações visuais são feitos nessa camada.
A responsabilidade principal de um container é buscar e/ou tratar os dados que a views irá
exibir, de maneira que ela já os receba da forma correta.
● Modules: Auxilia nas camadas de containers e views, serve para separar o que
pode ser escrito apenas com Javascript/Typescript, do que precisa de um
Framework como React. As pages são responsáveis por carregar os dados e
tratá-los, mas ela faz isso usando funções que estarão dispostas nos modules.
● Libs: Muitas vezes precisamos criar libs ou funções que resolvem problemas
importantes e não sabemos onde exatamente elas deveriam ficar, essa camada
também suporta esse tipo de implementação.
● 3rd Party: É algo muito importante para o futuro da aplicação e para a sobrevivência
ao tempo que ela consiga se manter atualizada, é muito importante evitar de criar
dependências de terceiros dentro da nossa aplicação, então, sempre que possível
vale adicioná-las nesta camada.
● Dto & Adapters: Nem sempre o backend está sendo escrito para atender ao
frontend, uma api pode servir diversos clientes, isso faz com que os dados nem
sempre fiquem como esperamos no frontend, para evitar de criar dependências com
as propriedades que o backend tem, podemos criar adapters quando buscar algo do
backend para manter a nossa consistência de tipos e um dto quando esses dados
forem retornar para api, dessa forma garantimos que os dados estarão sempre
consistentes no front e respeitando os contratos com o backend.
BASIC - CAMADAS E CONVENÇÕES
As camadas mostradas aqui e seus conceitos são levados também para os modelos
seguintes, algumas com pequenas alterações para melhor se adaptar, mas a ideia principal
de cada camada é sempre a mesma.
1. Components
2. Containers
a. Components
3. Views
4. Modules
a. useCases
b. Actions
c. Types
d. Endpoints
1. Components
2. Containers
Um container pode ter uma ou mais views e até mesmo um ou mais containers dentro dele,
buscando observar sempre o princípio da responsabilidade única.
Ex.:
Ele pode ter um container dentro dele para cada método de pagamento, ou seja:
Container de boleto: Recebe via props a configuração de pagamento para boleto e trata
esses dados da maneira que for necessária para serem exibidas na view de boleto.
Container de cartão: Recebe via props a configuração de pagamento para cartão, busca os
cartões salvos do usuário, trata os dados e repassa para view de cartão.
2.a - Components
Várias vezes nossos containers ou views ficam complexos a ponto de precisar realizar a
divisão dela em componentes, ou para serem reaproveitados em mais de uma
view/container, ou para simplesmente facilitar a leitura e manutenção do código do container.
Esses componentes serão específicos do contexto que está sendo implementado, então eles
devem ser colocados dentro desse contexto apenas, a pasta de componentes do container
serve para centralizar isso e garantir a organização do código.
3. - Views
As views tem como objetivo terem o mínimo de tratamento de dados possível, elas podem
ser reaproveitadas entre containers e também podem ter views dentro delas, mas por
conceito, nunca uma view vai ter um container dentro, a view é a última etapa entre o usuário
e a aplicação.
4. - Modules
Os Modules tem como principal objetivo separar as regras de negócio dos componentes
React, e também centralizá-las de uma maneira que possam ser reaproveitadas o máximo
possível.
4.a. - UseCases
4.b. - Actions
Elas são disparadas nos containers, views e componentes, são funções que geram
alterações no estado global da aplicação ou retornam um valor atualizado para o
estado local, seja através de comunicação com a api ou com a store, ela também vai
ter funções para realizar as consultas para api.
4.c. - Types
Centralização dos tipos que serão usados nos containers, views, actions e
componentes, essa centralização é importante tanto para evitar duplicação desses
tipos, quanto para garantir a consistência dos tipos entre os diferentes lugares, além
de evitar um ciclo de dependência em alguns casos.
4.d. - Endpoints
Visando sempre a separação de tudo que for possível, além de ajudar na manutenção
a longo prazo e também a documentação de quais recursos da api estamos
consumindo, esse arquivo centraliza todas as chamadas e endpoints da api
1. Config
a. Store
b. AsyncReducers
2. Components
a. 3. Containers
3. Containers
a. Components
4. Views
5. Modules
a. Actions
b. Reducers
c. Enums
d. Types
e. Endpoints
f. Dto
1.a. - Store
Com objetivo de manter na memória do client somente os reducers necessários, criamos uma
estrutura que permite adicionar os reducers de forma assíncrona, um reducer estático vai
estar sempre disponível em qualquer página da aplicação, enquanto os reducers assíncronos
só ficam disponíveis após serem instanciados utilizando o hook withAsyncReducers
- Reducer existe na lista nova e não existe na antiga: O novo reducer será adicionado
- Reducer existe na lista nova e existe na antiga: O reducer será mantido na store
- Reducer não existe na lista nova e existe na antiga: O reducer será apagado
2. Components
Um container pode ter uma ou mais views e até mesmo um ou mais containers dentro dele,
buscando observar sempre o princípio da responsabilidade única.
Ex.:
Ele pode ter um container dentro dele para cada método de pagamento, ou seja:
Container de boleto: Recebe via props a configuração de pagamento para boleto e trata
esses dados da maneira que for necessária para serem exibidas na view de boleto.
Container de cartão: Recebe via props a configuração de pagamento para cartão, busca os
cartões salvos do usuário, trata os dados e repassa para view de cartão.
3.a - Components
Várias vezes nossos containers ou views ficam complexos a ponto de precisar realizar a
divisão dela em componentes, ou para serem reaproveitados em mais de uma
view/container, ou para simplesmente facilitar a leitura e manutenção do código do container.
Esses componentes serão específicos do contexto que está sendo implementado, então eles
devem ser colocados dentro desse contexto apenas, a pasta de componentes do container
serve para centralizar isso e garantir a organização do código.
4. - Views
As views tem como objetivo terem o mínimo de tratamento de dados possível, elas podem
ser reaproveitadas entre containers e também podem ter views dentro delas, mas por
conceito, nunca uma view vai ter um container dentro, a view é a última etapa entre o usuário
e a aplicação.
5.a. - Actions
Elas são disparadas nos containers, views e componentes, são funções que geram alterações
no estado global da aplicação, seja através de comunicação com a api ou com a store, ela
também vai ter funções para realizar as consultas para api.
Actions síncronas
É uma função que é disparada e altera o estado da store sempre que não precisar de uma
confirmação ou aguardar uma resposta da api ou de terceiros.
Ex. O usuário clicou no botão de ativar o modo escuro, essa alteração afeta uma prop na
store diretamente, o resultado dessa alteração só é monitorado pelas views/containers, então
a action só tem a responsabilidade de informar a store que essa prop mudou.
Actions Assíncronas
É uma função que é disparada e altera o estado da store só após uma confirmação ou
aguardar uma resposta da api ou de terceiros.
Normalmente pensamos nelas quando existe algum loading que será mostrado pro usuário
REQUEST: Sempre que a action for chamada esse é o primeiro tipo que será disparado, ele é
responsável por ativar o estado de loading e "preparar" a store, após ela ser disparada deve
ser colocado um try/catch.
FUL_FILLED: No try fica a chamada da action de ful filled, esse é o caso de sucesso, ou seja,
se a action for na api ou num serviço de terceiro, quando esse resultado for positivo, essa
action é disparada com os dados de já preparados para serem populados na store e
removendo o estado de loading.
5.b. - Reducers
É ele que vai atualizar a store, é sempre imutável e só deve ser evoluído, além disso também
é importante ter um estado inicial (INITIAL_STATE) bem definido.
Os reducers são funções que recebem o estado atual, a action que foi disparada e retorna o
novo estado.
Vale lembrar que os reducers devem ser funções puras, todas as funções auxiliares que ele
utilizar devem ser funções que recebam parâmetros e retorne o valor alterado. Além disso, as
funções que são utilizadas no reducer devem estar declaradas no arquivo do próprio e
devem ser funções locais, ou seja, só serão utilizadas por esse próprio reducer.
5.c. - Enums
Os enums devem ser sempre únicos, eles são a "ligação" entre as actions e os reducers, vale
lembrar que quando uma action é disparada, todos os reducers recebem essa action, então
se os enums forem duplicados mais de um reducer será executado.
Para garantir que cada enum seja único, nós utilizamos o seguinte padrão:
[contexto] ação
ex.:
5.d. - Types
Centralização dos tipos que serão usados nos containers, views, actions e componentes,
essa centralização é importante tanto pra evitar duplicação desses tipos, quanto para garantir
a consistência dos tipos entre os diferentes lugares, além de evitar um ciclo de dependência
em alguns casos.
5.e. - Endpoints
Visando sempre a separação de tudo que for possível, além de ajudar na manutenção a
longo prazo e também a documentação de quais recursos da api estamos consumindo, esse
arquivo centraliza todas as chamas e endpoints da api
5.f. - Dto
Por vezes precisamos adaptar os nossos objetos no frontend para se adequar ao que a api
espera receber, para garantir a consistência do lado do frontend e a separação da
dependência com a api, esse arquivo é o único local que irá se adequar a api.
O dto idealmente vai exportar uma função dto, que recebe o objeto a ser tratado com o tipo
que o frontend utiliza e retorna esse objeto tratado e adaptado pra api.
ATENÇÃO: O dto só deve ser chamado no momento que a api for chamado, o retorno dele será
sempre direto para a api e não pode ser utilizado no frontend.
TESTES
Sempre devemos priorizar os testes unitários na aplicação, para facilitar isso, vamos dividir os
testes por camadas também:
Para testar todo o fluxo de container, view e redux precisamos dividir o teste em quatro:
- Container: Os testes devem ter os mocks das chamadas para api com consultas dos
dados, o mock da store e verificar se todas as views foram chamadas corretamente;
Caso existam chamadas de actions essas devem ser mockadas e testadas de maneira
que garanta que a action foi chamada com os parâmetros esperados e mockado o
seu retorno, caso este exista.
- View: Nos testes da view os dados devem vir através de props, validando o
comportamento que ela deve ter baseado nessas props;
Caso existam chamadas de actions essas devem ser mockadas e testadas de maneira
que garanta que a action foi chamada com os parâmetros esperados e mockado o
seu retorno, caso este exista.
- Actions: Os testes de actions devem ter os mocks para api e verificar se as actions
estão sendo disparadas da maneira que o redux espera.
- Reducer: Dado um estado inicial e uma action disparada com seu payload é esperado
um resultado final no reducer.
APOLLO - CAMADAS E CONVENÇÕES
1. Config
a. apollo
2. Components
3. Containers
a. Components
4. Views
5. Modules
a. Actions
b. Types
c. Dto
d. Queries
e. Vars
1.a. Apollo
3. Containers
Um container pode ter uma ou mais views e até mesmo um ou mais containers dentro dele,
buscando observar sempre o princípio da responsabilidade única.
Ex.:
Ele pode ter um container dentro dele para cada método de pagamento, ou seja:
Container de boleto: Recebe via props a configuração de pagamento para boleto e trata
esses dados da maneira que for necessária para serem exibidas na view de boleto.
Container de cartão: Recebe via props a configuração de pagamento para cartão, busca os
cartões salvos do usuário, trata os dados e repassa para view de cartão.
3.a - Components
Várias vezes nossos containers ou views ficam complexos a ponto de precisar realizar a
divisão dela em componentes, ou para serem reaproveitados em mais de uma
view/container, ou para simplesmente facilitar a leitura e manutenção do código do container.
Esses componentes serão específicos do contexto que está sendo implementado, então eles
devem ser colocados dentro desse contexto apenas, a pasta de componentes do container
serve para centralizar isso e garantir a organização do código.
4. - Views
As views tem como objetivo terem o mínimo de tratamento de dados possível, elas podem
ser reaproveitadas entre containers e também podem ter views dentro delas, mas por
conceito, nunca uma view vai ter um container dentro, a view é a última etapa entre o usuário
e a aplicação.
5.a. Actions
Elas são disparadas nos containers, views e componentes, são funções que geram alterações
no estado global da aplicação através das ReactiveVars, nela será importado o apolloClient
da config e dispara as queries.
5.b. - Types
Centralização dos tipos que serão usados nos containers, views, actions e componentes,
essa centralização é importante tanto pra evitar duplicação desses tipos, quanto para garantir
a consistência dos tipos entre os diferentes lugares, além de evitar um ciclo de dependência
em alguns casos.
5.c. - Dto
Por vezes precisamos adaptar os nossos objetos no frontend para se adequar ao que a api
espera receber, para garantir a consistência do lado do frontend e a separação da
dependência com a api, esse arquivo é o único local que irá se adequar a api.
O dto idealmente vai exportar uma função dto, que recebe o objeto a ser tratado com o tipo
que o frontend utiliza e retorna esse objeto tratado e adaptado pra api.
ATENÇÃO: O dto só deve ser chamado no momento que a api for chamado, o retorno dele será
sempre direto para a api e não pode ser utilizado no frontend.
5.d. Queries
Esse diretório fica responsável por centralizar todas as queries da aplicação, criando os
arquivos com a extensão .graphql no seguinte padrão [nome_da_query].graphql.
5.e. Vars
Para utilizar o apollo para controlar o estado global da aplicação usamos as duas funções
do apolloClient: makeVar e ReactiveVar.
Nesse arquivo nós criamos a variável que será usada como estado e a exportamos como
uma ReactiveVar do tipo que precisamos, exemplo: