Alura > Cursos de DevOps > Cursos de Mensageria/Streams > Conteúdos de Mensageria/Streams > Primeiras aulas do curso Kafka: idempotência e garantias

Kafka: idempotência e garantias

Organização e serviços de email - Introdução

Bem-vindo a mais um curso de Kafka, se você já fez os cursos anteriores, ou já tem o conhecimento dos cursos anteriores bem-vindo, bem-vinda, o que vamos fazer nesse curso? Vamos trabalhar com um conceito muito importante, algumas situações de falhas, como já vimos no passado, como podemos nos assegurar que as mensagens serão mesmo entregues.

E como podemos nos assegurar que a mensagem vai ser processada uma única vez com sucesso, se ela já foi processada com sucesso, eu não quero ter nenhum outro efeito colateral dentro do meu sistema.

Existe uma grande sacada, podemos utilizar certos Ids ou certas configurações do Kafka para que alcancemos isso, eu vou mostrar algumas dessas abordagens e vamos implementar nelas, ficando com um sistema ainda mais complexo, no sentido de mais rico e com mais serviços acontecendo, trabalhando por lá e suportando coisas mais interessantes ainda.

Como por exemplo, quando um usuário ou uma usuária aperta o botão de compra várias vezes no meu aplicativo, eu quero que a compra seja processada uma única vez, se ficar dando F5 ou “Ctrl + R” no navegador eu quero que a compra seja processada uma única vez e não importa, não quero processar mais vezes e por aí vai.

Vamos ver como garantir esse tipo de situação entre diversas outras coisas, vamos ver tudo isso nesse curso.

Organização e serviços de email - Organização e lidando com múltiplos tópicos de envio em um mesmo serviço

[[00:00] Vamos continuar o nosso projeto, eu vou abrir aqui no IntelliJ o projeto como estava no final do curso anterior, lembrem-se, você já tem que ter o conhecimento dos cursos anteriores para fazer esse curso, ou ter feito os cursos anteriores e continuar do ponto em que estávamos, se você já tem o conhecimento dos cursos anteriores e quero fazer esse, você pode baixar nos exercícios o código como estava no curso anterior.

Eu tenho aqui o meu diretório com o projeto já baixado, eu vou abrir ele aqui no meu IntelliJ e eu tenho um projeto para trabalhar, o nosso projeto, recapitulando, é um sistema de e-commerce com diversos módulos, um módulo que é uma biblioteca comum de Kafka, isto é, que trabalha com o Kafka e depois eu tenho vários serviços.

Você poderia argumentar que eu gostaria de ter de repente uma biblioteca comum para acessar banco de dados, pode ser que todos os bancos de dados usem o mesmo banco, parece ruim, cada um tem o seu banco nos meus serviços, parece ser o tradicional, cada serviço tem o seu banco, mas a camada de código que abstrai o acesso ao banco pode ser uma biblioteca commom, seja lá o que for de DataBase.

Temos esse tipo de bibliotecas commom e depois vários serviços, alguns serviços que são pontos de entrada do nosso cliente conosco, por exemplo, o nosso service-http-ecommerce o que é quando a pessoa faz requisições http, também temos o service-new-order que a pessoa executa um programa para valer.

Eu poderia ter também outras requisições http, seja lá o que for, que seriam pontos de entrada automáticos, como um API REST ou algo do gênero, que são outros programas se comunicando comigo, um aplicativo, um celular se comunicando comigo, são serviços que são pontos de entrada no meu grande conjunto de serviços.

Tem os serviços que ficam lá no miolo, o fraud detection, ele recebe uma mensagem e ele produz outras mensagens, que ficam lá no miolo, tem alguns serviços que são meio que utilitários, por exemplo aquele que roda um Batch, o serviço de Users, tem dois serviços lá dentro, um que roda um Batch de envio de mensagens, ele recebe uma mensagem que ele tem que replicar para todos os usuários, ele republica essa mensagem em outro tópico para todos os usuários, por exemplo.

Eu tenho também serviços que são uma “camada final”, eles se comunicam com sistemas externos, por exemplo, temos um serviço na parte de e-mail que envia um e-mail, ele se comunica com um sistema externo SMTP para enviar e-mail, não implementamos o SMTP aqui, mas você faz como você quiser para acessar a API SMTP, ou um serviço de e-mail que você utiliza, MailChimp, seja lá o que for.

Também podemos ter serviços que fazem requisições para outros sistemas http, para outros sistemas com outros protocolos, podemos ter serviços que armazena o log em disco, eu estou considerando o disco como uma coisa externa, ou que armazena um Log em um sistema externo, tudo isso são maneiras de nos comunicarmos nesse nosso universo de serviços.

Agora eu quero dar uma organizada nesses pacotes do nosso common-kafka, que é uma coisa simples de organizar, um conjunto pequeno de classes.

Seria razoável separarmos um pouco disso e tem duas formas que eu acredito que são mais comuns de separar, uma forma seria separar a parte de recebimento de mensagens com a parte de envio de mensagens, isto é, o que está ligado com serviço e com deserialização, colocar em um pacote, a parte que está ligada com o Dispatcher e Serialização colocar em outro pacote, uma maneira razoável de organizar.

Outra maneira de organizar seria separar a parte de Serialização e Deserialização, a parte que lida com o Gson da parte que lida com o Kafka em si, percebem as diferenças? Então uma parte vai trabalhar com o Gson, isso é uma coisa, outra coisa é a API do Kafka em si que são o KafkaDispatcher e o KafkaService de envio e recebimento de mensagem, você poderia separar de maneiras diferentes.

Eu prefiro separar da primeira maneira que eu citei e eu prefiro deixar o GsonDeserializer que é utilizado junto com o nosso KafkaService, eu prefiro deixá-los juntos no mesmo pacote, o GsonDeserializer junto com o KafkaDispatcher porque eles são usados no mesmo pacote, pessoalmente eu prefiro dessa maneira, de novo, não tem regra.

Eu vou criar um novo pacote, um deles eu vou chamar de “dispatcher” que é a parte que envia mensagens, a parte de envio de mensagens é o KafkaDispatcher com o GsonSerializer, serialização e o Dispatcher estão juntas, aí você fala: “E a mensagem?” A mensagem é geral então eu vou deixar geral, eu só queria dar essa organizada.

Vamos refatorar, ele comenta que tem vários conflitos de coisas que são locais e que vão ter que ser transformadas em públicas, realmente eu quero que esses caras tenham uma interface pública a partir de agora, vamos abrir esses dois caras? Vamos ver o que tem que ser público aqui.

Construiu um KafkaDispatcher, tem que ser público, o KafkaDispatcher pode ser usado em qualquer pacote, o método send eu quero que seja usado em qualquer pacote, fechei, a Message que está em outro pacote agora, também “Ctrl + Enter” eu quero que ela seja pública, o Dispatcher eu resolvi, o Serializer agora, o Serializer é público.

Agora vamos fazer a parte do serviço que consome, eu vou colocar um Package, vou chamar de “consumer”, dentro do consumer eu vou colocar o Service, o Deserializer e o ConsumerFunction que isso daqui só é usado no consumo, os outros são usados nas duas fases, na de ida e na de volta então eu vou deixar separado, estou movendo tudo para lá.

A interface ConsumerFunction é pública porque está escrito e o método é público porque é público, aqui está public, lembrando, aquela interface eu deixei um throws Exception que pode ser algo muito nojento, porém no nosso caso que é muito específico, queremos ser obrigados a tratar todo tipo de Exception e temos já todo tipo de Exception sendo jogado, vários tipos que herdam de Exception então acabou ficando Exception, por isso.

Nossa classe Service tem que ser pública por que podemos acessá-la de qualquer lugar, os construtores também, se era privado pode continuar privado, o run, quem é que chama o run? A gente ainda chama o run para começar a rodar o KafkaService, vamos deixar public void run.

Reparem que ele é um Closeable mas ela não é um runable, se ele fosse um runable o que podíamos fazer? Chamar uma nova thread nele e ele chamava o run, dá para transformarmos ele em um runable? Até dá, o problema do runable é que não pode jogar Exception teríamos que tratar esse erro de alguma maneira para parar a thread, ela até para se der esse erro mas através de um Runtime Exception, poderia, não é o foco agora, estamos só movendo as classes agora e corrigindo o aspecto de acesso aos métodos.

Estamos com o pacote correto, eu posso dar um Build, Rebuild Project, para ele Rebuildar o projeto inteiro e temos isso refatorado, aqui ele reclama porque nesse caso específico faltou passarmos um CorrelationId, vamos passar um Correlation Id? Lembra, NewOrderMain está começando, se está começando eu vou querer um CorrelationId novo, então vou passar um “id” novo, como esse id vai ser usado? Para essas duas mensagens, para mensagem do Order e do emailDispatcher.

O nosso “id” é um “new CorrelationId” e qual é o nome mesmo? É o nosso “NewOrderMain.class.getSimpleName”, reparem que se eu usar dessa maneira aqui, o que acontece? Ele vai gerar um id aleatório, mas na hora que formos mandaram o send, vai ser exatamente o mesmo id para os dois, exatamente, eu não queria, eu queria que fosse um pouco diferente deste para este.

Por quê? Para identificar que este está ligado com esse tópico e esse com esse outro tópico, então aí sim está uma refatoração que eu gostaria de fazer agora e a refatoração que eu gostaria de fazer é a seguinte: quando eu envio a mensagem, lembra quando enviamos? Até usamos um CorrelationId e se der uma olhada no CorrelationId, temos como concatenar com mais alguma coisa usando o continueWith.

O que eu vou fazer? Percebam que estamos em uma situação no NewOrderMain que foi rodado, para essa rodagem do NewOrderMain eu criei um CorrelationId que é único, mas eu preciso Identificar qual é o tópico que estou usando e mais ainda, se eu tivesse mandando três mensagens de emailDispatcher eu queria identificar o tópico e qual dessas três está sendo usada.

O que eu vou fazer? Na hora que eu chamo o send, eu tenho o meu CorrelationId já com o nome do título e um Id aleatório, vou concatenar nesse meu Id, eu vou “continueWith” e eu coloco o próprio tópico, só que para separar o que é o tópico e o que não é o tópico, porque lembra, o continueWith vai colocar um hífen, para identificar que é um tópico e não um processo que está executado, eu vou colocar só um “”_ “ + topic”, o ID desse envio dessa mensagem e vai o próximo serviço.

Vamos tentar um Build, Rebuildar tudo, está Rebuildando, ele vai projeto por projeto, da base para todos os outros e todos os projetos estão Buildados, o que eu vou querer fazer agora? Fizemos uma pequena refatoração de pacotes e demos uma olhada no CorrelationId e em um cenário que ainda tinha um problema que é, quando eu tenho vários envios de mensagem de dois tópicos distintos na mesma invocação daquele serviço, ia ficar com o mesmo id nos dois casos, eu não quero o mesmo id nos dois casos.

Agora eu tenho id sempre únicos, inclusive Se eu mandar dez vezes a mesma mensagem, se dentro do NewOrderMain o que eu fizesse era enviar 10 vezes, o que eu vou ter? Dez CorrelationId e aqui vai ter um primeiro começo igual, o tópico, um valor aleatório e vai para frente.

Qual é o meu próximo passo? Discutir um pouco o envio de e-mail, eu quero discutir agora com vocês essa nossa camada final, como podemos estruturar isso e como isso costuma ser estruturado. Quando eu tenho um serviço externo, por exemplo e-mail, poderia ser Analytics, poderiam ser várias outras coisas que podemos ter, mas um caso clássico é e-mail, que é um sistema que temos uma comunicação razoavelmente complexa.

Temos que criar o conteúdo do e-mail, temos que criar o subject, temos que enviar esse e-mail para uma pessoa, ou para todas as pessoas, talvez customizar o email por pessoa, talvez o e-mail seja transacional, um pessoa efetuou a compra, talvez seja um email de marketing de um dia, hoje é dia 01/01 então eu quero dar feliz Ano Novo para todo mundo.

Tem tipos de e-mail, situações, queremos deixar isso espalhado por todos os serviços, queremos concentrar isso, como queremos lidar com isso? Vamos discutir isso daqui a pouco, Como podemos fazer isso com serviços e mensagens.

Organização e serviços de email - Micro serviços de email e fast delegate real

Continuando nosso projeto, eu queria discutir um pouco a partir do e-mail, repare, o nosso service-email foi feito de uma maneira que, qualquer parte dessa grande aplicação, envia uma mensagem e já dispara um e-mail, nesse instante o e-mail é só uma string, mas poderiam ser quatro strings, de quem, para quem, os tópicos, o subject e o corpo, o body, pelo menos esses quatro poderiam compor um e-mail e enviamos.

Mas você fala: “Guilherme, precisa mesmo de um serviço para isso? Não é só criar uma biblioteca chamada common-email e nessa biblioteca common-email eu crio uma classe chamada Email que recebe esses quatro valores no construtor, tem um método send ou algo do gênero, uma função em uma linguagem funcional, que tem o send, que recebe quatro strings e envia o e-mail de forma assíncrona?” Poderia ser, eu poderia implementar isso através de uma biblioteca, como fizemos com o CommonKafka.

Qual é a dificuldade que teríamos em lidar com isso? A dificuldade é que o e-mail é um sistema externo, por exemplo, mesmo sendo um sistema interno poderíamos ter esse problema, ele é um sistema externo, como nos comunicamos com ele, é importante.

Eu posso ficar mandando mensagens para esse cara que envia e-mail a cada segundo? Ou ele vai achar que eu sou um spam e é melhor eu enviar um limite de 100 por minuto? Eu devo me comunicar com os servidores da Amazon ou do Azure? Percebam que têm perguntas que talvez eu queira centralizar em um único lugar, se eu deixar esta biblioteca espalhada em todos os projetos, se eu tiver que fazer uma mudança dessas, eu tenho que mudar todos os projetos.

Eu não estou usando mais o serviço de e-mail da Amazon, eu tenho que usar o serviço do outro, não tenho como, enquanto todos não atualizarem a CommonLibrary do service-email1.0 para o 2.0, que só mudou a implementação da Amazon para o Google Email ou alguma coisa do gênero para enviar e-mails, já era, não vai, vai estar usando metade, metade.

Agora se isso está extraído em um serviço de e-mail, fica transparente, se estamos usando servidores de SMTP próprios, se estamos usando os servidores da Amazon, servidores do Google, da Microsoft, da IBM, seja lá de onde for, não importa.

Por quê? O que me importa é, eu enviei uma mensagem para o meu serviço interno dizendo que eu quero enviar um e-mail, alguém que é responsável pela equipe de e-mail é responsável por lidar com essas partes e eu estou de boa, feliz e contente porque eu pedi para o e-mail ser enviado, o e-mail tem que ser enviado, é isso, tem uma equipe responsável pelo e-mail, claro, às vezes somos responsáveis por tudo, não tem problema.

Reparem que o problema da biblioteca é esse, todo mundo têm que migrar ao mesmo tempo, por quê? Porque eu estou utilizando provedores de serviços externos que eu tenho que migrar de um para o outro e eu dependo de migrações e lembrem, aquela sincronização, imagina em que eu tenho a 10 serviços, se eu tiver 300, eu não vou conseguir imigrar todos ao mesmo tempo, nunca, fica difícil de fazer isso.

De que maneira eu poderia fazer isso? Eu tenho duas maneiras pensando em um serviço de e-mail, uma é a maneira atual em que por exemplo, o meu cara que deseja enviar um e-mail, quem deseja enviar um e-mail por exemplo? Quando temos um pedido de compra, logo de cara, na nossa Servlet por exemplo, já me envia um e-mail dizendo, quero enviar um e-mail e colocamos uma única string.

Lembram que eu falei que poderiam ser quatro strings em um e-mail? Poderia ser uma estrutura de dados Email com quatro Strings, nós criamos o texto customizado e mandamos para o EmailService e o EmailService dispara esse e-mail.

Na verdade preparamos tudo e só avisamos o EmailService, eu tenho isso para enviar, envie para mim por favor? E o EmailService envia, por exemplo, eu tenho um arquivo que eu quero disponibilizar para alguém acessar, se eu tenho esse arquivo que foi gerado na minha máquina e eu quero disponibilizar para uma pessoa acessar no e-mail, eu tenho duas abordagens.

Posso fazer isso via attachment, eu posso adicionar um attachment no e-mail e vai junto com o e-mail e posso fazer isso através de um link, isto é, eu tenho que pegar esse arquivo que eu gerei na minha máquina, subir para um lugar que vai ser público - ou não público, não sei, preciso estar logado, não precisa - e esse arquivo vai ser linkado dentro do e-mail.

Percebem que já vou ter vários passos que eu tenho que fazer, o que podemos fazer? Ao invés de eu ficar gerando todo texto do e-mail, eu mesmo em cada um dos meus mil serviços, eu crio um tipo de serviço intermediário, que é o serviço que sabe, por exemplo, é o serviço responsável por e-mails em determinada situação, tem um serviço de e-mail que realmente só recebe os dados crus e dispara, porque com isso isolamos a camada externa.

Se você tem um serviço que tem que se comunicar com um serviço externo da Receita Federal, de nota fiscal, isola isso em um sistema, mas se de repente você tem 100 caras se comunicando com eles, talvez você queira extrair algum deles, talvez você queira isolar algum desses.

O que eu gostaria de fazer agora? Reparem que em alguns desses serviços eu vou querer enviar um e-mail ou em outros serviços eu poderia querer enviar um e-mail, ao invés de eu gerar um texto desse e-mail e enviar direto para o meu EmailService, eu simplesmente falo, eu tenho um novo pedido de compra, vamos voltar no nosso NewOrdem.

Ele simplesmente fala ECOMMERCE_NEW_ORDER, acabou, não existe esse ECOMMERCE_SEND_EMAIL, por que não vai existir isso? Porque se isso existir aqui eu vou ter que, nesse serviço de NewOrder, me preocupar tanto com o processo de uma nova compra em si, quanto com uma questão de e-mail, como bolar este e-mail, são duas preocupações em um único serviço.

Vai ter gente que vai argumentar que temos que quebrar isso em dois, tem empresas brasileiras e lá fora que fazem esse caminho, então eu queria mostrar esse cenário, como eu quebro isso para ter em dois? Já tem alguém escutando o ECOMMERCE_NEW_ORDER, quem é que escuta o ECOMMERCE_NEW_ORDER? O FraudDetector, o log, o analítico, várias coisas podem escutar.

Eu posso ter um outro serviço, eu vou querer ter outro módulo e esse módulo é um service e o que ele vai fazer? Ele vai enviar um e-mail quando tem uma nova ordem de compra, quando temos um ECOMMERCE_NEW_ORDER, temos um service-email-new-order.

O que esse service-email-new-order faz? Você verá como ele vai ficar simples, ou pequeno, ele simplesmente faz o seguinte, vai ficar escutando um tópico, que nem o fraud-detector, ele é mais complexo até do que precisamos o fraud-detector, ele ficará escutando um tópico, como fazemos aqui, eu vou copiar o fraud-detector, vou copiar tudo, quando eu der o paste ele vai só pastear o pacote ecommerce, como eu imaginava, eu vou dar uma mudança manual br.com.alura.ecommerce.

Lembrando, eu poderia estar criando um pacote por serviço, organizar no serviço também pacotes, não tem problema esse eu estou deixando realmente no mesmo por enquanto, vamos mover não está querendo corrigir para mim, temos agora as duas classes.

Ao invés de FraudDetectorService o que eu quero fazer é renomear, eu quero renomear para “EmailNewOrderService”. Então o que o EmailNewOrderService faz? Ele vai criar o “emailService” para mim, emailService::parse, vai criar o KafkaService, eu tenho que adicionar a dependência, vai chamar o run, ele escuta o ECOMMERCE_NEW_ORDER.

E qual é a diferença? Ao invés dele fazer o que está fazendo agora, o que ele vai fazer é: pedir para despachar um e-mail, eu vou tirar essas linhas do NewOrder e o que ele faz é, quando ele recebe no meu parse, ele está processando uma nova ordem, então estamos “preparing email”, preparando o e-mail.

Eu vou jogar o value e o resto eu não vou imprimir, eu vou direto simplesmente fazer o quê? Despachar o meu e-mail, eu preciso de um emailDispatcher, eu vou deixar um aqui para mim, lembrando que o emailDispatcher é um Dispatcher de string, então eu vou enviar uma String, aqui eu tenho que ter o e-mail que eu vou enviar, que é o e-mail da pessoa, então é o “record.value()” e temos o “getPayLoad()” que é uma order e tem o “getEmail()”, temos tudo isso para pegar.

Como essa é a minha order, eu já vou extrair minha order, vou extrair “order”, pode ser uma “var”, o IntelliJ não pegou isso automático, aqui eu vou deixar o record mesmo que é a nossa mensagem, eu acho que é mais bonito e o que eu vou usar de Hash? Eu vou usar por usuário, “order.getEmail()”, como eu tenho o email, vou usar o e-mail do usuário como hash.

Desculpa, o hash do e-mail eu já estou usando aqui, o que faltou foi o CorrelationId. O CorrelationId é o quê? É o id da minha mensagem, continua com “EmailNewOrderService.class.getSimpleName()”, eu tenho o meu novo id, para esse id não ficar muito longo, eu vou extrair essa variável como “id” e para que isso também não fique, eu vou extrair esse record.value como a minha mensagem, essa é a minha mensagem, também pode ser um var.

Eu tenho uma mensagem, posso deixar tudo bonito, aqui ficou feio eu vou deixar um pouco bonito, o que esse service é capaz de fazer mesmo? Ele escuta agora a única mensagem que é enviada por ordem, teve um pedido de ordem, ele escuta uma mensagem, quando temos o pedido de ordem, agora temos aquele Fast Delegate de verdade, só dispara uma mensagem, acabou.

O que ele faz aqui dentro mesmo? Aqui dentro ele prepara o e-mail e pede para o e-mail ser enviado, quer dizer, se eu precisasse fazer upload de arquivos, faço upload de arquivos aqui, se eu precisar customizar esse e-mail de cinco maneiras diferentes, eu customizo de cinco maneiras diferentes, se eu precisar enviar um e-mail para o comprador, um e-mail para o vendedor, se de repente meu site é um Marketplace, tem o comprador e a compradora, o vendedor e a vendedora, eu quero disparar dois e-mails, disparo os dois aqui.

Percebeu a diferença? Eu isolei no meu serviço de e-mail, no meu serviço de nova compra, eu isolei todos os processos, inclusive o processo de preparar os e-mails, aqui eu só estou preparando os e-mails, seja preparar o e-mail para quem comprou, preparar o e-mail para quem vendeu, preparar o e-mail para seja lá quem for, eu isolei isso, se eu isolei isso aqui, lá no nosso HttpEcommerce o que tínhamos mesmo? Tínhamos uma Servlet que era uma nova compra.

O que podemos fazer ali? Arrancar fora o e-mail, por quê? Por que não despachamos mais e-mail, só despachamos ECOMMERCE_NEW_ORDER. Esse nosso emailDispatcher não precisa mais, não precisamos mais desse cara, posso fechar esse, posso fechar este outro, aqui também devemos ter um emailDispatcher que não é usado mais.

Será que um “Ctrl + Enter” dá certo? Eu nunca usei dessa maneira, não dá, tem que remover mesmo, depois realinhar, estou feliz assim, ficou redondo, agora eu tenho um Fast Dispatche e o Delegate também pegou, delegou, estou feliz e eu isolei entre os meus serviços e um serviço de e-mail que é externo, um serviço intermediário.

De novo, não precisa ser para todos serviço externo colocar esse serviço intermediário, não precisa ser que só com serviço externo você vai querer este cara intermediário, você vai perceber se aquele código que você está fazendo, por exemplo, preparando 5 e-mails para serem enviados, faz sentido ser em ordem síncrona e sequencial, isto é, uma linha depois da outra ou não, distribui, se o e-mail for, foi, senão foi, não foi, pelo menos eu continuei o processamento da minha compra, mostrei a tela de sucesso e fui para frente.

Percebem as diferenças? Não estou preocupado, já comecei o processo da compra, se o e-mail de repente não foi, depois damos um jeito, quando você acessar o banco já vai estar lá, estou feliz, se eu armazenei no banco, disparo a mensagem e sigo em frente, ou só dispara a mensagem, a mensagem armazena no banco e sigo em frente, o que você preferir.

Tendo aqui uma mensagem que está registrada no Kafka, que eu sei que eu não vou perder, estou feliz com isso, Fast Delegate isolando o serviço de e-mail, uma abordagem bem comum, aí você fala: “Guilherme, eu tenho 20 tipos de e-mail no meu sistema, eu vou criar 20 serviços? Mais o serviço de e-mail, 21?”.

Essa seria essa abordagem, não é à toa que existem empresas onde se tem mais serviços do que desenvolvedores, por quê? Porque vários desses serviços são minúsculos, são tipo esse daqui.

Claro, esse daqui poderia utilizar uma biblioteca common-email que só te ajuda a criar aqueles quatro campos, o from, o to para deixar com o do usuário ou da usuária, para deixar o texto super redondo, poderia usar a biblioteca common para isso aqui onde fizer sentido, se é aqui ou se é no serviceEmail, você vai perceber onde faz sentido para você este tipo de biblioteca, esse tipo de ajuda.

Mas a sacada é, realmente vão ter muitos serviços minúsculos, que basicamente não tem consumo de CPU, de vez em quando vai precisar de CPU e é muito fácil de manter porque basicamente são uma ou duas classes que fazem alguma coisa bem específica de forma assíncrona sem nos preocuparmos, perceberam porque cresce bastante a quantidade de serviços quando você começa a trabalhar com realmente micro serviços? E aqui na mensageria podemos trabalhar dessa maneira.

Sobre o curso Kafka: idempotência e garantias

O curso Kafka: idempotência e garantias possui 146 minutos de vídeos, em um total de 28 atividades. Gostou? Conheça nossos outros cursos de Mensageria/Streams em DevOps, ou leia nossos artigos de DevOps.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Mensageria/Streams acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas