Camada de Integração
Introdução
A integração entre frontend e backend ocorre via API (Application Programming Interface) através de endpoints webservice.
O consumo de webservices é implementado por meio de funções JavaScript, aqui chamadas de “Remotes”, descritas a seguir.
Estrutura de Arquivos
As funções de serviços remotos são organizadas por diretórios conforme o exemplo a seguir. Cada arquivo de módulo contém 1 (uma) função remota, relativa a determinado endpoint do backend.
remotes/
arquetipo/ <-- nome do microsserviço
apoio/ <-- assunto ou agrupador
pesquisarMotivosCancelamentoRemote.js
pesquisarSituacoesOsRemote.js
pesquisarTiposExecutoresRemote.js
executor/
cadastrarExecutorRemote.js
inativarExecutorRemote.js
pesquisarExecutoresRemote.js
reativarExecutorRemote.js
ordemServico/
atestarOrdemServicoRemote.js
cancelarOrdemServicoRemote.js
encaminharOrdemServicoRemote.js
executarOrdemServicoRemote.js
gerarOrdemServicoRemote.js
glosarOrdemServicoRemote.js
pesquisarOrdensServicoRemote.js
Remotes
A seguir são definidas as diretrizes para funções de serviços remotos.
-
Retornam
Promise
que resolve o objeto do JSON ouundefined
para webservices que retornam corpo HTTP vazio. -
Consomem somente 1 (um) endpoint remoto.
-
Tem nome igual ao nome da operação remota, sem o sufixo da versão do endpoint. Exemplo: a função
pesquisarExecutores()
consome o serviçopesquisarExecutores.vN
. -
São implementadas no arquivo de módulo JavaScript que recebe o mesmo nome da função mais o sufixo
Remote
. Exemplo:pesquisarExecutoresRemote.js
. -
São exportadas de maneira nomeada (Named Exports). Exemplo:
export { pesquisarExecutores }
. -
O módulo JavaScript correspondente é localizado na pasta
app/remotes/<nomeMicrosservicoRemovo>/<grupo>
. -
Não implementam regras de negócio.
-
Tratam erros de conexão.
-
Tratam erros HTTP, por exemplo, códigos 4xx e 5xx.
-
Utilizam o
fetch
nativo ou preferencialmente o componente da arquiteturaNetworking.fetch
.- O
Networking.fetch
é uma extensão dofetch
nativo que permite a injeção automática de parâmetros comuns, como o token de autenticação JWT e parâmetros adicionais na URL que identificação o nome e versão da aplicação de frontend.
- O
Exemplo 1 (GET)
-
Função remota que pesquisa dados (GET) e retorna um DTO
-
Arquivo
app/remotes/arquetipo/ordemServico/pesquisarOrdensServicoRemote.js
-
Endpoint do backend
<baseUrl>/pesquisarOrdensServico.v3?descricao=<valor>&situacaoId=<valor>...
-
No fluxo de sucesso, o backend retorna código 200 e um JSON
Código-fonte:
import { validateResponse } from '@/app/util/remoteUtils';
import { Objects, Networking, getProfile } from '@bernardo-dias/react-cloudsupport';
// Vide Swagger do backend
// Parâmetros: descricao, situacaoId, de, qtd, ordem
// Retorno: lista, de, qtd, total
const pesquisarOrdensServico = (params = {}) => {
// Prepara os parâmetros
const urlParams = new URLSearchParams(Objects.cleanNullUndefined(params));
// Prepara a URL
const url = `${getProfile().wsArquetipo}/pesquisarOrdensServico.v3?${urlParams}`;
// Executa a requisição
return Networking.fetch(url)
// Erro http
.then(validateResponse)
// Converte json para objeto
.then(response => response.json());
};
export { pesquisarOrdensServico }
A função
Objects.cleanNullUndefined
retorna uma nova versão do objeto porém sem os atributosnull
eundefined
.A função
getProfile
obtém as configurações de ambiente da aplicação.A função
validateResponse
realiza o tratamento de erros de conexão e de HTTP, vide exemplo ao final da seção.Note que há
return
emNetworking.fetch
, para garantir que a camada de apresentação possa manter o encadeamento de Promises.
Exemplo 2 (POST)
-
Função remota que altera dados (POST) e retorna
undefined
-
Arquivo
app/remotes/arquetipo/ordemServico/encaminharOrdensServicoRemote.js
-
Endpoint do backend
<baseUrl>/encaminharOrdemServico.v1?protocolo=<valor>&executorUid=<valor>...
-
No fluxo de sucesso, o backend retorna código 200 e corpo HTTP vazio
Código-fonte:
import { validateResponse } from '@/app/util/remoteUtils';
import { Objects, Networking, getProfile } from '@bernardo-dias/react-cloudsupport';
// Vide Swagger do backend
// Parâmetros: protocolo, executorUid, observacao
// Retorno: (não se aplica)
const encaminharOrdemServico = (params = {}) => {
// Prepara os parâmetros
const postParams = JSON.stringify(Objects.cleanNullUndefined(params));
// Prepara a URL
const url = `${getProfile().wsArquetipo}/encaminharOrdemServico.v1`;
// Prepara options do fetch
const options = {
method: 'POST',
body: postParams,
headers: {'Content-Type': 'application/json'},
};
// Executa a requisição
return Networking.fetch(url, options)
// Erro http
.then(validateResponse)
// Sem retorno
.then(_ => {});
};
export { encaminharOrdemServico }
A função
Objects.cleanNullUndefined
retorna uma nova versão do objeto porém sem os atributosnull
eundefined
.A função
getProfile
obtém as configurações de ambiente da aplicação.A função
validateResponse
realiza o tratamento de erros de conexão e de HTTP, vide exemplo ao final da seção.A estrutura
_ => {}
é apenas uma boa prática que evita que o objetoresponse
seja exposto para a camada de apresentação, tal que a função remota retornáundefined
, vez que o corpo da resposta HTTP, neste exemplo, é vazio.Consulte a documentação oficial do JavaScript para conhecer todas as configurações do
options
dofetch
.
Exemplo 3 (validações)
Exemplo de implementação da função validateResponse
para um backend em Cloudsupport
:
const _isStringNotEmpty = attr => typeof attr === 'string' && attr.length;
// Esse método trata os códigos padrões de erro de webservice conforme convenções
// do Cloudsupport. Não se aplica ao Websupport.
const validateResponse = response => {
// Erros de controle de acesso
if (response.status == 401) {
throw Error('É necessário realizar autenticação');
}
if (response.status == 403) {
throw Error('O acesso não foi autorizado');
}
// Erro de endpoint inexistente
if (response.status == 404) {
throw Error('O serviço solicitado não existe');
}
// Erro de negócio
if (response.status == 422) {
return response.json()
.catch(_ => { throw Error('O servidor retornou formato inválido') })
.then(err => {
// Erro de BusinessException
let mensagem = err.message ?? '';
// Erro de validação declarativa
if (Array.isArray(err.fieldMessages) && err.fieldMessages.length > 0) {
if (_isStringNotEmpty(mensagem))
mensagem += ': ';
mensagem += err.fieldMessages.map(item => item.message).join("; ");
}
// Mensagem padrão
if (mensagem.length == 0)
mensagem = 'Ocorreu um problema ao processar a requisição';
// Lança o erro com atributo adicional
throw Object.assign(new Error(mensagem), {fieldMessages: err.fieldMessages});
});
}
// Outros erros de frontend
if (response.status >= 400 && response.status < 500) {
return response.json()
.catch(_ => { throw Error('O servidor retornou formato inválido') })
.then(err => { throw Error(`A requisição enviada é inválida: ${err.tracking}`) });
}
// Erros de backend
if (response.status >= 500 && response.status < 600) {
return response.json()
.catch(_ => { throw Error('O servidor retornou formato inválido') })
.then(err => { throw Error(`Ocorreu um erro ao processar a requisição: ${err.tracking}`) });
}
return response;
};
export { validateResponse }
A função considera convenções da arquitetura sobre retorno de webservices do backend. Em síntese:
-
Em caso de erro de regra de negócio (BusinessException) é retornado o código HTTP 400 e o campo
message
é incluído no JSON de retorno, para que seja exibido para o usuário final. -
Em caso de erro inesperado no backend, o
Cloudsupport
omite o campomessage
, inclui um código de rastreamento no campotracking
e retorna HTTP 500.
Consulte a documentação de Webservices para mais informações sobre os padrões de webservices da arquitetura no que tange o backend.
Próximos Passos
A próxima leitura sugerida é a seção Segurança, que detalha a utilização do padrão OpenID Connect.
A seção Documentação da API contém a referência completa de todos os módulos e componentes da arquitetura.