Angular em uma Arquitetura de Microsserviços
03/02/2021
Na PITANG, durante minha passagem pelo programa do Operador Nacional dos Sistemas Elétricos (ONS), fiz parte do projeto SAAT (Sistema de Administração e Apuração da Transmissão), que consiste em uma arquitetura de microsserviços, onde temos uma aplicação .NET Core para cada caso de uso, que por sua vez, possui também uma aplicação Angular específica.
Essa configuração mostra que é necessário uma lógica para que cada caso de uso foque em seus requisitos e que serviços em comum sejam compartilhados, nos concedendo um grande sistema funcionando com muita reutilização de código, performance e mantenabilidade.
Estrutura
O primeiro desafio é abstrair toda a parte genérica que cabe a cada Frontend. E quando se trata de Angular, deve-se pensar primeiramente em quais módulos cada aplicação têm em comum.
Sendo assim, a solução acaba por ser uma adaptação de um padrão muito comum em Angular, utilizando módulos Core, Shared e Feature, que são, resumidamente:
- O Core Module seria o responsável por conter a importação de todo serviço injetável em comum da aplicação;
- O Shared Module, onde contém a importação de todos os módulos e arquivos essenciais para o funcionamento de um Feature Module;
- O Feature Module oferece um conjunto de funcionalidades focadas em um requisito específico do sistema;
- E assim, o Root Module, que é por padrão nomeado como app.module.ts, é o responsável por unir todas as Features e serviços em comum.
Porém, como o ambiente do SAAT envolve múltiplas aplicações, cada Feature será como um Root Module. E o módulo compartilhado, estará agora em um pacote NPM de uma biblioteca Angular, que será uma dependência de cada caso de uso ( figura 2). Como as aplicações são independentes, dispensamos a ideia de um Core Module.
O Shared Module, denominado no projeto como SAAT Module, é responsável por toda a exportação dos módulos locais e globais que são fundamentais para o funcionamento das aplicações, como: BrowserAnimationsModule, ReactiveFormsModule e HttpClientModule, módulo de Interceptação HTTP (que é próprio do SAAT) e outros (figura 3).
Módulos Locais
Entre as aplicações, existem diversos comportamentos que são comuns a todas, como a interceptação de requisições, para tratamento de erro, definição do status de carregamento e manipulação do token de autorização. Assim, toda essa lógica é implementada dentro da biblioteca, e cada aplicação se encarrega apenas de fazer algumas importações para que mantenham o mesmo comportamento e possuam serviços importantes únicos e centralizados.
Como por exemplo o SAATInterceptorModule, que adiciona duas funcionalidades de interceptação HTTP a partir de diferentes serviços. No SAAT existe o Interceptor responsável pela tradicional adição do JWT (JSON WEB TOKEN) ao cabeçalho das requisições, e outro para tratar erros e o estado das requisições HTTP.
Acima, uma demonstração de como funciona um dos interceptors, o de tratamento de erros e gerenciamento da tela de loading. O serviço HttpStatusService pode ser acessado por qualquer caso de uso para que consiga mostrar ou esconder a tela de carregamento.
As requisições feitas por cada aplicação Angular passarão pelos mesmos serviços de interceptação, e assim, a responsabilidade de cada caso de uso fica mais centrada apenas em seus requisitos.
Integração com a biblioteca
Na figura 6, um exemplo (com nomes fictícios) de como é a integração da biblioteca com uma aplicação de um dos casos de uso. Visivelmente, o módulo fica mais limpo e só manipula arquivos que sejam próprios do caso de uso.
Para concluir a integração, é necessário também que cada aplicação proteja o roteamento com um serviço que implementa o método de checagem de permissão canActivate. Nele, o serviço de verificação JWT é acessado e é definido se o usuário está ou não autenticado.
Injeção de Dependência
No processo de centralização de serviços e componentes, surge um problema: o serviço de interceptação de JWT precisa de informações de endpoints para fazer requisições e redirecionamentos. E como o sistema possui múltiplos ambientes: produção, homologação e desenvolvimento, o serviço em questão necessita de uma referência da variável de ambiente da aplicação Angular que está em execução.
A solução está na utilização do princípio Dependency Injection (DI). O Angular possui um framework próprio de injeção de dependências, e com a utilização de tokens injetáveis, é possível passar para a biblioteca a variável de ambiente, e assim, injetar em qualquer serviço ou componente que necessite de suas propriedades (figura 9).
A referência da variável de ambiente é passada pelo método forRoot e é registrado como um provider da biblioteca (figura 6 e 8).
E assim, a biblioteca guarda em si uma grande flexibilidade quanto as aplicações e tendo também não só serviços, como foi mostrado em exemplos aqui, mas também componentes, diretivas, funções auxiliares e entre outras funcionalidades que são reutilizáveis por cada caso de uso.
Todos os diagramas deste artigo foram feitos utilizando a ferramenta draw.io.