Event-Bus no Frontend Parte 1: Comunicação entre componentes no Angular

Event-Bus no Frontend Parte 1: Comunicação entre componentes no Angular

Comunicação é algo muito importante em nossos dias, não concorda? Estamos sempre nos comunicando, seja por meios eletrônicos ou por gesto e expressões faciais. Tudo hoje se comunica por algum meio. Isso não é diferente quando falamos em desenvolver software. Seja no backend ou frontend, precisamos ter algum tipo transmissão de informações, seja para fora do nosso software, ou dentro de seus componentes. No frontend isso é muito importante! Vamos falar um pouco sobre essa abordagem e entender melhor como podemos fazer componentes se conversarem de uma forma bem desacoplada entre si. Mas para entender bem o assunto não podemos pular etapas, precisamos entender os conceitos iniciais. Vamos começar com um pattern conhecido, o mediator!

Analogia

O Mediator é um padrão de projeto comportamental que permite que você reduza as dependências caóticas entre objetos. O padrão restringe comunicações diretas entre objetos (componentes).

Vamos ver uma analogia para exemplificar melhor o que ocorre quando não temos um mediador.

Pilotos de aeronaves que se aproximam ou partem da área de controle do aeroporto não se comunicam diretamente entre si. Ao invés disso falam com um controlador de tráfego aéreo, que está alocado em uma torre alta perto da pista de aterrissagem. Sem o controlador do tráfego aéreo os pilotos precisariam estar cientes de cada avião nas redondezas do aeroporto, discutindo as prioridades de aterrissagem com um comitê de muitos outros pilotos. Isso provavelmente aumentaria em muito as estatísticas de acidentes aéreos. A torre de controle tem um objetivo claro, facilitar a comunicação de cada aeronave que precisa trafegar pela pista. Ela não tem a responsabilidade de controlar o voo do começo ao fim. Apenas recebe as notificações e sinaliza para outros pilotos se podem ou não decolar ou pousar.

O mesmo acontece com componentes e este padrão ajuda você a eliminar dependências mútuas entre várias classes.

Componentes não devem estar cientes de outros componentes. Se algo importante ocorrer, ele deve apenas notificar o mediador sobre sua situação atual. Quando o mediador recebe a notificação, ele pode facilmente identificar o remetente, o que é suficiente para decidir que componente deve ser acionado em retorno.

Da perspectiva de um componente, não existem outros componentes 🥲. O remetente não sabe quem vai receber seu pedido e o destinatário não sabe quem enviou o pedido.

Certo mas tudo parece indicar que esse Pattern é semelhante a outro chamado Observer. Vamos ver rapidamente algumas diferenças?

Observer vs Mediator

A diferença entre o Mediator e o Observer pode parecer nebulosa. Na maioria dos casos, você pode implementar qualquer um desses padrões; mas às vezes você pode aplicar ambos simultaneamente.

O objetivo primário do Mediator é eliminar dependências múltiplas entre um conjunto de componentes do sistema. Ao invés disso, esses componentes se tornam dependentes de um único objeto mediador. O objetivo do Observer é estabelecer comunicações de uma via dinâmicas entre objetos, onde alguns deles agem como subordinados de outros. Novamente:

Observer: É utilizado quando uma ação (evento) ocorre em uma classe observada que precisa produzir (transmitir) uma reação em outra classe observadora, mas não é recomendado que a classe observada seja acoplada à classe observadora. Exemplo, a classe A, pode ter zero ou mais observadores registrados nele. Quando algo em A ocorre, como um evento de click do mouse ou receber dados novos de uma API, ele notifica todos os observadores que se inscreveram para receber aquelas notificações.

Mediator: Este promove o acoplamento fraco, evitando que os objetos se refiram explicitamente uns aos outros e permite que você varie sua interação de forma independente. Exemplo. O software possui 2 componentes: um botão "Desconectar" e uma barra de status. Quando o usuário pressionar o botão "Desconectar", a barra de status mostrará "Desconectado do servidor". Aqui o mediador serve como um manipulador dos dois componentes, bem como manipulador da implementação lógica. O mediador sabe que o botão e a barra de status existem e sabe que a barra de status deve ser atualizada quando o botão for clicado.

Estas são as diferenças. Você pode ler muito mais sobre Mediator se desejar. Além disso caso queira entender um pouco melhor sobre o Observer, já escrevi em outro momento no artigo Entendendo o padrão Observer utilizando RxJS.

Agora que entendemos um pouquinho sobre essas diferenças de patterns e como podem nos ajudar a estabelecer comunicação dentro do nosso software, vamos para a explicação do Event Bus na teoria.

Abordagem do Event Bus

Podemos chamar também de barramento de eventos ou simplesmente message bus. O que importa é o conceito por trás e para iniciar, veja a imagem abaixo:

Design sem nome.png

Temos dois componentes que precisam se comunicar, o componente B (direita da imagem) precisa emitir eventos para sinalizar alguma mudança de dados que ocorreu para outros que estão interessados nesse evento.

Um Event Bus nos permitirá enviar dados entre diferentes componentes e será quase um tipo de mediador. Como o componente B (direita da imagem) quer falar com outro componente que talvez nem conheça, ele pode enviar dados através de eventos e, em seguida, o outro componente pode obter esses dados. Então o barramento de eventos vai ser o mecanismo de comunicação entre os componentes, como mencionado segue o padrão mediador, mas aqui vamos utilizar o Subject do RxJS para nos ajudar a fazer isso.

Primeiro, o componente A (esquerda da imagem) vai se inscrever em um evento (subscriber) do Event Bus:

Design sem nome-2.png

No nosso exemplo, teremos um evento chamado CustomerSelected.

Captura de Tela 2022-04-29 às 01.33.50.png

Agora o componente B vai gerar um evento (raise event), por exemplo, enviou dados sobre um cliente, como cidade e idade para o Event Bus.

Design sem nome-3.png

O componente B (direita da imagem) não sabe quem está ouvindo o evento que ele gerou. Na verdade, ele não tem ideia se alguém está ouvindo neste momento. Está apenas dizendo: ei, Event Bus, ocorreu um evento e preciso passar esses dados para quem estiver interessado. Agora, supondo que o componente A na esquerda tenha se inscrito, ele obterá os dados do evento e poderá usar esses dados para exibir da maneira que desejar.

Recapitulando:

  1. Temos dois componentes, A e B, que não sabem da existência um do outro.

  2. Precisamos que ao ocorrer um gatilho de evento transmitindo dados do Cliente, que esse evento gerado no componente B chegue até A que esta inscrito.

  3. Não queremos que ocorra um acoplamento entre componentes, por isso utilizamos um Observer, que apenas recebe e transmite os eventos para os interessados.

  4. A classe EventBus não deve conhecer Lógica dos dois componentes, mas apenas notificar os eventos que ocorreram em um componente para o outro.

  5. Estamos utilizando o Subject do RxJS para nos ajudar, ele é quem escuta esses eventos.

Conclusão

Espero que tenha ficado claro na teoria o objetivo do Event Bus. É importante lembrar que essa implementação pode não atender certas necessidades da aplicação, dependendo do modelo de negócio e dimensões de outros fatores, muito provavelmente precisamos de outros tipos de abordagem para comunicação entre as peças do aplicativo para detectar mudanças de estados. Mas está abordagem funciona bem para aplicativos que tem uma necessidade muito alta de comunicação interna entre componentes.

Estou elaborando um projeto base para termos uma implementação em código do que foi abordado. Nossos próximos artigos quero trazer isso escrito e funcionando no Framework Angular.

Fico por aqui, caso tenha uma sugestão de melhoria, críticas construtivas ou dúvidas, fique à vontade para entrar em contato. Até o próximo Post! 🚀