Arquitetura BLoC com Flutter
1. O que é a arquitetura BLoC?
A arquitetura BLoC (Business Logic Component) permite uma separação da logica de negócio da sua aplicação e da UI através do uso de Streams. Construímos toda a lógica de negócios com essa streams, o que nos permite o uso de bibliotecas Reactive como a RxDart.
Nós podemos fazer streams que se originam da arquitetura BLoC e podemos ter streams que se conectam da UI para a BLoC.
2. O que são streams?
Em suma, uma fonte de eventos assíncronos.
O conceito de streams é meio complicado, para isso, vamos usar uma analogia.
Vamos imaginar uma fábrica de queijo. Dentro da nossa fábrica, o que vamos produzir? Isso mesmo, queijos, no entanto, nossa fábrica é um pouco especial, quando disse que era uma fábrica que fazia queijos, é porque ela APENAS produz queijo, então, se alguém chegar e pedir outro tipo de alimento, desculpa, mas fazemos APENAS queijo .
Nossa fábrica não tem nenhum escritório remoto e se a pessoa quiser fazer um pedido, terá que ir diretamente e escrever um formulário solicitando um queijo, do TIPO, apenas. Então, nosso cliente preencheu o formulário, agora ela vai ter que entregar para o supervisor de formulário.
E qual o papel do nosso supervisor? Receber o formulário que foi escrito e levar para o interior da nossa fábrica para dar início ao processo. Então o pedido (request) é entregue ao supervisor e o mesmo envia-o para o inspetor de pedidos da fábrica.
Assim que a fábrica recebe o pedido, ele é analisado pelo inspetor. Ele vai coletar o tipo (type) de queijo que foi solicitado. O inspetor não se importa com mais nada, nem com o tamanho do queijo, nem para quem é, com nada, APENAS com o tipo do queijo. Uma vez que o inspetor tem o tipo, ele repassa para o cozinheiro.
Nosso cozinheiro irá analisar e vai dizer: “É queijo? Se sim, vou fazer, senão, não vou fazer e ainda vou emitir uma nota de erro para o cliente que solicitou e enviar para o ponto de coleta.” Então, não importa o que aconteça, nosso cliente terá que vir até a fábrica buscar seu queijo. Sim, nossa fábrica não faz entrega.
Caso o pedido tenha sido feito corretamente ou não, o pedido vai ser levado e deixado, no que eu posso chamar de: “Ponto de coleta”. Logo, é função do nosso cliente, ir até o ponto de coleta e ver se seu queijo está pronto ou não. Se por algum motivo o queijo não tenha sido feito, é por que o cliente errou quando preencheu o formulário e escreveu o TIPO (type) errado de alimento. Logo, para ele ter o seu queijo, terá que refazer o pedido (request), com o tipo (type) correto e esperar a resposta (response) no ponto de coleta.
Características da nossa fábrica de queijos.
1.1 Nossa fábrica recebe um ‘pedido’, processa, e então envia o pedido para outro ponto.
1.2 Nossa fábrica não é construída assim que o cliente aparece. Ela já está construída.
1.3 A fábrica passa muito tempo esperando para um pedido chegar. Assim que o pedido chega e é feito, nossa fábrica não desaparece logo após, ela é reutilizada para futuros pedidos.
1.4 Alguém tem que estar por perto esperando por um pedido chegar. Esse é o nosso ponto de partida (entry point) para o interior da fábrica.
1.5 Após o queijo ser feito, alguém tem que ir buscar ele. O queijo vai ser deixado no ponto de coleta, caso tenha sido feito e o cliente poderá levar seu queijo pra casa. Caso não, será deixado uma mensagem de erro solicitando que o cliente refaça seu pedido com o tipo correto de queijo.
Agora, vamos modelar nossa fábrica de queijos usando Dart.
Continuando, precisamos declarar nossa função main e começar o nosso código.
StreamController possui dois objetos adicionais já criados: sink (supervisor de entrega) e stream (inspetor de pedidos).
Sink: é a habilidade de adicionar novos dados para serem processados pela nossa stream. Comparando com nossa analogia, é o nosso supervisor de pedidos. O pedido não é enviado diretamente para a stream, apenas entregue ao sink para depois o próprio sink entregar a stream.
Stream: É a função que terá como incumbência, levar o pedido recebido até o cozinheiro da fábrica.
Pronto, agora que nosso supervisor de pedidos já trouxe o formulário para o inspetor, vamos referenciar nossa stream para que o pedido seja repassado ao cozinheiro:
Agora só precisamos modelar nosso cozinheiro e criarmos uma função para que ele possa preparar o queijo solicitado pelo cliente:
Em resumo…
Nosso inspetor de pedido, sendo mais específico, é a função do StreamController ‘sink.add()’. Ela recebe novos tipos de dados que serão levados e processados pela stream que criamos.
A stream recebe e processa esse pedido. A função ‘map’ da stream olha para o valor que recebe, processa, retorna o tipo do pedido e ai sim, leva para o cozinheiro.
O cozinheiro, vulgo ‘StreamTransformer’, observa o tipo que foi enviado, processa, prepara e então envia de volta para a stream.
A grande diferença entre as funções ‘map’ e ‘StreamTransformer’, é que na ‘map’ function temos apenas a proporção de 1:1 em termos de pedidos que podem ser recebidos e retornados para a stream. Enquanto com a ‘StreamTransformer’, nós recebemos um tipo de queijo, porém, podemos produzir mais. A função ‘sink.add()’ é mais flexível quanto a isso.
Esse ‘sink’ presente na função ‘controller.sink.add()’ serve para iniciarmos nosso processo. Enquanto o sink presente na função ‘handleData’ ele adiciona um valor logo abaixo da linha 47, que é aonde nosso cozinheiro atua. Então, se chamarmos a função ‘sink.add()’ dentro do nosso transformador, ele não inicia o processo, ele apenas adiciona um novo valor para o cozinheiro.
No fim, tudo vai para o posto de coleta (listen function), seja o queijo ou uma mensagem de erro do cozinheiro (transformer).
Nosso ponto de coleta retorna uma mensagem informando que seu queijo está pronto. E a função ‘onError’ é chamada quando se tem presente algum tipo de erro durante a produção e processamento, no nosso caso, uma string diferente do valor ‘cheese’.
Em termos práticos, essa arquitetura BLoC pode ser comparada com o padrão de projeto ‘factory method’.