O que é uma arquitetura de front-end escalável?

Quando se trata de desenvolvimento de software, os dois significados mais comuns da palavra escalabilidade estão relacionados ao desempenho e à manutenção de longo prazo de uma base de código. Você pode ter ambos, mas focar na boa manutenção facilitará o ajuste do desempenho sem afetar outras partes do aplicativo. Isso é ainda mais verdadeiro no primeiro plano, onde temos uma distinção importante do back-end: o estado local.

Nesta série de artigos, falaremos sobre como desenvolver e manter um aplicativo front-end escalável usando métodos testados na vida real. A maioria dos nossos exemplos usará React e Redux, mas frequentemente compararemos com outras pilhas de tecnologia para mostrar como você pode obter o mesmo efeito. Vamos começar falando sobre a arquitetura desta série, a parte mais importante do seu software.

O que é Arquitetura de Software?
O que exatamente é arquitetura? Pode parecer pretensioso dizer que a arquitetura é a parte mais importante do seu software, mas me escute.

Arquitetura é como você faz com que as várias unidades de seu software interajam para destacar as decisões mais importantes que você precisa tomar e adiar decisões menores e detalhes de implementação. Projetar a arquitetura de um software significa separar o aplicativo real de suas tecnologias de suporte. Seu aplicativo real não tem conhecimento de bancos de dados, solicitações AJAX ou GUIs; em vez disso, ele é composto de casos de uso e unidades de domínio que representam os conceitos cobertos por seu software, independentemente das funções nas quais os casos de uso são executados ou onde os dados é persistido.

Há algo mais importante para falar sobre arquitetura: ela não implica organização de arquivos, nem como você nomeia arquivos e pastas.


Camadas no desenvolvimento front-end
Uma maneira de separar as coisas importantes das menos importantes é usar camadas, cada uma com responsabilidades diferentes e específicas. Em uma arquitetura baseada em camadas, uma abordagem comum é dividi-la em quatro camadas: aplicativo, domínio, infraestrutura e entrada. Essas quatro camadas são melhor explicadas em outro artigo "NodeJS and Good Practices". Recomendamos que você leia a primeira parte do post sobre eles antes de prosseguir. Você não precisa ler a segunda parte, pois é específica para NodeJS.

As camadas de domínio e aplicação não são tão diferentes entre front-end e back-end porque são independentes de tecnologia, mas não podemos dizer o mesmo para as camadas de entrada e infraestrutura. Em navegadores da Web, a camada de entrada geralmente tem apenas uma função, a visualização, portanto, podemos até chamá-la de camada de visualização. Além disso, o front-end não tem acesso a um banco de dados ou mecanismo de enfileiramento, portanto, não os encontraremos em nossa camada de infraestrutura de front-end. O que encontraremos são abstrações que encapsulam solicitações AJAX, cookies de navegador, LocalStorage e até mesmo unidades para interagir com servidores WebSocket. A principal diferença é apenas algo que é abstraído, então você pode até ter repositórios de front-end e back-end com exatamente a mesma interface, mas com diferentes tecnologias por baixo. Você pode ver o quão incrível uma boa abstração pode ser?

Não importa se você usa React, Vue, Angular ou qualquer outra coisa para criar suas visualizações. É importante seguir as regras da camada de entrada sem qualquer lógica, delegando assim os parâmetros de entrada para a próxima camada. Há mais uma regra importante sobre arquiteturas baseadas em camadas de front-end: para que a camada de entrada/visualização esteja sempre sincronizada com o estado local, você deve seguir um fluxo de dados unidirecional. Este termo soa familiar? Podemos fazer isso adicionando uma quinta camada especializada: estado, também conhecido como armazenamento.


Camada de estado
Ao seguir um fluxo de dados unidirecional, nunca alteramos ou modificamos os dados recebidos por uma exibição diretamente na exibição. Em vez disso, despachamos o que chamamos de "ações" da exibição. Funciona assim: uma ação envia uma mensagem para a fonte de dados, a fonte de dados se atualiza e reenvia para a view com os novos dados. Observe que nunca há um caminho direto da visualização para o armazenamento, portanto, se duas subvisualizações usarem os mesmos dados, você poderá despachar ações de qualquer subvisualização, o que fará com que ambas as subvisualizações sejam renderizadas novamente com os novos dados. Parece que estou falando especificamente de React e Redux, mas não é o caso; você pode obter o mesmo resultado com praticamente qualquer framework ou biblioteca front-end moderna, como React + API de contexto, Vue + Vuex, Angular + NGXS , ou mesmo Ember, Use o método down-action de dados (também conhecido como DDAU). Você pode até fazer isso com jQuery, usando seu sistema de eventos para enviar ações!

Essa camada é responsável por gerenciar o estado local e em constante mudança de seu front-end, como dados buscados no back-end, dados efêmeros criados no front-end, mas ainda não persistidos ou informações transitórias, como o estado de uma solicitação. Caso você esteja se perguntando, esta é a camada onde residem as ações e manipuladores responsáveis ​​por atualizar o estado.

Embora muitas vezes vejamos regras de negócios e definições de caso de uso na base de código colocada diretamente na ação, se você ler a descrição das outras camadas com cuidado, descobrirá que já temos um lugar para colocar nossos casos de uso e regras de negócios, não a camada de estado. Isso significa que nossas ações agora são casos de uso? não! Sim. Então, como devemos tratá-los?

Vamos pensar...dissemos que Action não é um caso de uso, e já temos uma camada para colocar nossos casos de uso. A visão deve despachar a ação, a ação recebe as informações da visão, as entrega ao caso de uso, despacha uma nova ação de acordo com a resposta e, finalmente, atualiza o estado - atualiza a visão e fecha o fluxo de dados unidirecional. Essas ações soam como controladores agora? Eles não são o lugar para obter parâmetros da exibição, delegar ao caso de uso e responder com base no resultado do caso de uso? É exatamente assim que você deve tratá-los. Não deve haver nenhuma lógica complexa ou chamadas AJAX diretas aqui, pois elas são responsabilidade de outra camada. A camada de estado deve saber apenas como gerenciar o armazenamento local, nada mais.

Há outro fator importante em jogo. Como a camada de estado gerencia o armazenamento local consumido pela camada de exibição, você notará que os dois estão um pouco acoplados. Haverá alguns dados somente para exibição na camada de estado, como um sinalizador booleano indicando se uma solicitação ainda está pendente, para que a exibição possa exibir um controle giratório, o que é perfeitamente correto.

código:

import api from './infra/api'; // has no dependencies
import { validateUser } from './domain/user'; // has no dependencies
import makeUserRepository from './infra/user/userRepository';
import makeArticleRepository from './infra/article/articleRepository';
import makeCreateUser from './app/user/createUser';
import makeGetArticle from './app/article/getArticle';

const userRepository = makeUserRepository({
  api
});

const articleRepository = makeArticleRepository({
  api
});

const createUser = makeCreateUser({
  userRepository,
  validateUser
});

const getArticle = makeGetArticle({
  userRepository,
  articleRepository
});

export {
  createUser,
  getArticle
};
export default ({ validateUser, userRepository }) => async (userData) => {
  if(!validateUser(userData)) {
    throw new Error('Invalid user');
  }

  try {
    const user = await userRepository.add(userData);
    return user;
  } catch(error) {
    throw error;
  }
};
export default ({ api }) => ({
  async add(userData) {
    const user = await api.post('/users', userData);

    return user;
  }
});

Você notará que as partes importantes, os casos de uso createUser etc., são instanciados no final do arquivo e são os únicos objetos que são exportados, pois serão injetados na Action. O restante do seu código não precisa saber como o repositório foi criado e como ele funciona. Realmente não importa, é apenas um detalhe técnico.
Não importa para o caso de uso se o repositório envia uma solicitação AJAX ou persiste algo no LocalStorage; não é responsabilidade do caso de uso saber disso. Se você quiser usar o LocalStorage enquanto sua API ainda está em desenvolvimento e depois passar a usar chamadas online para a API, desde que o código que interage com a API siga a mesma interface do código que interage com o LocalStorage, você não precisa Não é necessário alterar seu caso de uso.

Mesmo que você tenha dezenas de casos de uso, repositórios, serviços, etc., você pode fazer a injeção manualmente conforme descrito acima. Se construir todas as dependências for muito confuso, você pode usar um pacote de injeção de dependência, desde que não aumente o acoplamento.

Uma regra prática para testar se seu pacote de DI é bom o suficiente é verificar se passar de uma abordagem manual para o uso da biblioteca não requer tocar em nada além do código do contêiner. Nesse caso, o pacote é muito problemático e você deve escolher um pacote diferente. Se você realmente deseja usar um pacote, recomendamos o Awilix. É bastante simples de usar e improvisado, apenas tocando no arquivo contêiner. Há uma série muito boa sobre como usá-lo e por quê, escrita pelo autor do pacote. 

Acho que você gosta

Origin blog.csdn.net/weixin_44786530/article/details/130194702
Recomendado
Clasificación