Camada de Integração

Introdução

A integração entre microsserviços ocorre via API (Application Programming Interface).

Nesse contexto, a presente arquitetura oferece duas maneiras principais para implementação de integração. São elas:

  • Via webservices: Na qual as aplicações, então clientes, consomem webservices de outras aplicações. A integração por webservices oferece algumas vantagens, como:

    • Simplicidade de implementação, pois as classes que consomem webservices, aqui chamadas de “Remotes”, são quase que triviais.

    • Monitoramento e análise de tráfego facilitados, em razão de o protocolo HTTP ser amigável às tecnologias de observabilidade, tal como tracing distribuído.

  • Via broker de mensageria, como AMQP: Através da construção de tópicos de distribuição de mensagens. Esse modelo é preferido quando há necessidade de:

    • Roteamento avançado de requisições, onde, por exemplo, um comando precisa ser entregue a vários microsserviços de destino; e/ou

    • Maior performance; e/ou

    • Tolerância a interrupções na comunicação ou indisponibilidade de microsserviço, pois o broker tem capacidade de persistir as mensagens e entregá-as aos destinatários quando eles se recuperarem.

Outras formas de integração poderão ser implementadas a depender de requisitos técnicos específicos. Um microsserviço não deve acessar diretamente o banco de dados de outro microsserviço, salvo em cenários especiais.

Estrutura de Pacotes

A camada de integração tem a seguinte estrutura de pacotes:

Pacote Descrição
.remotes.nomeMicrosservicoRemoto.nomeFuncionalidade Contém as classes clientes para comunicação com outros microsserviços ou serviços externos.

Estereótipos de Classes

As classes de serviço que fazem a implementação do lado cliente de um endpoint webservice são chamadas de “Remotes”. A arquitetura oferece o estereótipo @Remote para essa finalidade.

A seguir são definidas as diretrizes para classes de serviços remotos.

@Remote

  • Herdam de BaseRemote e são anotadas com @Remote.

  • Têm sufixo Remote.

  • São singletons e stateless, não devendo manter estados em seus atributos.

  • Consomem somente 1 (um) endpoint remoto.

  • São localizadas no pacote .remotes.{nomeMicrosservicoRemovo}.{nomeFuncionalidade} de acordo com o caminho do endpoint.

  • Recebem e retornam dados na forma de DTOs, localizados no mesmo pacote da classe Remote.

  • Têm nome igual ao da operação do endpoint.

  • Utilizam preferencialmente WebClientComponent do Cloudsupport para fazer a comunicação webservice, apresentado a seguir.

Exemplo:

@Remote
public class EnviarNotificacaoRemote extends BaseRemote() {

  @Inject WebClientComponent component;

  public EnviarNotificacaoRetorno enviarNotificacao(EnviarNotificacaoParams params) {
    return component
             .authenticatedPost("/notificacoes/enviarNotificacao.v1")
             .contentType(MediaType.APPLICATION_JSON)
             .bodyValue(params)
             .retrieve()
             .bodyToMono(EnviarNotificacaoRetorno.class)
             .timeout(Duration.ofSeconds(5))
             .block();
  }
}

No exemplo, o serviço remoto é o “enviarNotificacao”, na sua versão “v1”, disponibilizado pelo microsserviço “notificacoes”.

WebClientComponent

O componente WebClientComponent é recomendado para consumo de webservices e oferece os seguintes recursos:

  • Utiliza internamente singleton do WebClient do SpringBoot, portanto compatível com pilhas Servlet e Reactive, com suporte nativo a pool de conexões e requisições não bloqueantes, visando maior performance.

  • Faz a resolução da URL do endpoint a partir de propriedades do profile, permitindo que o código-fonte se adapte conforme o ambiente, por exemplo, local, homologação ou produção.

  • Fornece os métodos que propagam o Access Token OAuth2 (ex: JWT) do contexto do Spring Security, bem como os cabeçalhos “X-Forwarded-For” e “X-Forwarded-Proto”, sendo úteis para consumo de webservices dos demais microsserviços da aplicação, tal que a autenticação do usuário é mantida ao longo do fluxo distribuído de requisições webservices.

O WebClient utilizado pelo componente é construído sobre o WebClient padrão da aplicação, o que permite a incorporação de incrementos, como balanceadores de carga e monitoramento.

Resolução de Rota

A configuração do apontamento do serviço remoto é realizada nas propriedades de profile com prefixo cloudsupport.remotes.route. É possível configurar em nível de microsserviço, ou por operação específica, conforme a seguir.

Configurando o apontamento de uma operação específica:

cloudsupport.remotes.route.notificacoes.enviarNotificacao=https://dominio:porta

Configurando o apontamento de todas as operações de um microsserviço:

cloudsupport.remotes.route.notificacoes=https://dominio:porta

Os arquivos de profile ficam na pasta resources da aplicação, separados por ambiente: application-<ambiente>.properties ou application-<ambiente>.yml.

Operações

As operações disponíveis no componente são:

Método Descrição
authenticatedGet(endpoint) Prepara uma requisição HTTP GET “autenticada” para o endpoint informado, com resolução de URL baseada no profile, propagação de Bearer Token (ex: JWT) e propagação dos cabeçalhos “X-Forwarded-For” e “X-Forwarded-Proto”. Para uso entre microsserviços confiáveis, tipicamente aqueles que compõem o mesmo sistema. Retorna o tipo de objeto do método uri() do WebClient do SpringBoot.
authenticatedPost(endpoint) Prepara uma requisição HTTP POST “autenticada” para o endpoint informado, com as mesmas funcionalidades do authenticatedGet.
get(endpoint) Prepara uma requisição HTTP GET para o endpoint informado, com resolução de URL baseada no profile, porém sem propagação de Bearer Token (ex: JWT) ou dos cabeçalhos “X-Forwarded-For” e “X-Forwarded-Proto”. Para uso no consumo de serviços externos à aplicação, em que não se deseja propagar dados de autenticação do usuário. Retorna o tipo de objeto do método uri() do WebClient do SpringBoot.
post(endpoint) Prepara uma requisição HTTP POST para o endpoint informado, com as mesmas funcionalidades do get.
defaultClient() Retorna o WebClient interno, singleton, utilizado nas operações get() e post() do componente. Pode ser utilizado para modificar (mutate) o WebClient e adicionar recursos, como filtros.
authenticated() Retorna o WebClient interno, singleton, utilizado nas operações authenticatedGet() e authenticatedPost() do componente. Pode ser utilizado para modificar (mutate) o WebClient e adicionar recursos, como filtros.

Estendendo o WebClient

Modificar (mutate) o WebClient utilizado pelo WebClientComponent para adicionar recursos, como filtros, ou configurações.

O exemplo abaixo demonstra a modificação do WebClient para suportar requisições maiores que 256KB:

@Inject WebClientComponent component;

private WebClient withMemory(int mb) {
  component
    .defaultClient()
    .mutate()
    .codecs(codecs -> codecs
      .defaultCodecs()
      .maxInMemorySize(mb * 1024 * 1024))
    .build();
}

Neste caso, ao utilizar o WebClient estendido, a resolução de URL de endpoint pode ser assim implementada:

@Inject RemoteRouteResolver routeResolver;

// ...
withMemory(2)
.get()
.uri(routeResolver.getRoute("/notificacoes/enviarNotificacao.v1"))
// ...

Próximos Passos

A próxima leitura sugerida é a seção Segurança, que detalha a utilização do padrão OpenID Connect.