Olá! Meu nome é Maximillian Arruda e serei o instrutor neste curso de persistência poliglota com Java.
Audiodescrição: Maximillian é um homem pardo, com barba, olhos e cabelos castanhos. Ele usa óculos e veste uma camiseta do SouJava.
Neste curso, nós vamos aprender a implementar a persistência poliglota em nossas soluções. Vamos desenvolver uma arquitetura, pois isso vai além de apenas integrar bancos de dados; é necessário compreender como isso funciona e entender os trade-offs envolvidos.
Vamos explorar um banco de dados relacional, suas capacidades, forças e fraquezas, além de um banco de dados de documentos, suas limitações e benefícios. Também abordaremos bancos de dados chave-valor, colunar e de grafos, compreendendo como eles se comunicam. Analisaremos uma arquitetura hexagonal para entender como ela pode ser integrada da melhor forma possível.
Faremos isso de maneira prática, seguindo um projeto chamado PolyAnalyzer, que será migrado para uma arquitetura poliglota. Este curso não termina aqui; é importante aproveitar a plataforma, participar dos fóruns, comunidades e atividades relacionadas para estar preparado para desenvolver sistemas no mundo moderno e em ambientes cloud (nuvem).
Esperamos que já sejamos pessoas desenvolvedoras plenas, capazes de progredir de forma autônoma no desenvolvimento, para acompanhar todo o processo, entender os trade-offs (compensações) e aplicar isso no dia a dia. Estamos prontos para implementar uma arquitetura poliglota? Nos vemos na próxima aula. Até mais!
Bem-vindos à nossa primeira aula do curso de Persistência Poliglota com Java. Neste curso, vamos explorar e implementar juntos uma arquitetura de persistência poliglota utilizando diferentes bancos de dados, atendendo assim aos diversos desafios do desenvolvimento de softwares modernos.
Na maioria das vezes, estamos envolvidos em projetos já existentes, onde a arquitetura está pronta, as decisões já foram tomadas e as tecnologias escolhidas, incluindo a persistência do banco de dados. Isso é comum no dia a dia de uma pessoa engenheira de software, especialmente em sistemas empresariais, onde decisões como essas foram feitas há anos, quando a opção por um banco de dados relacional era unanimidade. Naquela época, optar por um banco de dados relacional fazia todo sentido. Os data centers eram caros, o armazenamento também, e a internet ainda estava em desenvolvimento, não era como é hoje. Muitas empresas investiam fortemente nessas infraestruturas, então economizar espaço e garantir a integridade dos dados era uma prioridade.
Por volta de 1960, surgiu o modelo estruturado, que sucedeu os plain old files (arquivos antigos simples). Depois, veio o modelo de rede, que ligava dados usando ponteiros, mas ainda não era o ideal. Com o modelo relacional, as coisas mudaram, pois ele trouxe uma estrutura tabular, utilizando tabelas, colunas e tuplas, com uma estrutura bem definida de dados, permitindo consistência. Naquela época, também percebemos que era possível evoluir a aplicação separadamente dos dados, que até então estavam muito atrelados à aplicação. A integração entre aplicativos, utilizando arquivos, resultava em muitos problemas de truncamento de dados. O banco de dados relacional, com seu modelo, foi uma virada de chave, fazendo todo sentido a modificação.
O banco de dados relacional trouxe outras características, como as propriedades ACID (atomicidade, consistência, isolamento e durabilidade), que caracterizam todos os sistemas transacionais. Com esse modelo, criou-se também uma linguagem de consulta e manipulação de dados, o SQL. Diversos fornecedores começaram a desenvolver suas plataformas de persistência e sistemas gerenciadores de bancos de dados usando essa tecnologia.
Falando em tecnologia, várias linguagens e frameworks foram construídos utilizando essa tecnologia de banco de dados relacional. Temos ferramentas como clientes, drivers e assim por diante. A tecnologia pode não estar implementada de uma forma específica, mas se comunica sob o mesmo protocolo, que é o SQL. Hoje, temos o SQL 2023, se não me engano, com um padrão ANSI, mas cada fornecedor tem sua liberdade poética para ajustar e atender ao seu domínio.
Naquela época, fazia muito sentido ter servidores centralizados e uma escala vertical, em vez de horizontal. Para aumentar o processamento, era necessário investir em mais infraestrutura na máquina, como adicionar mais CPU, memória e armazenamento, além de contar com um datacenter. No entanto, com a chegada da cloud (nuvem) e da internet, essa dinâmica mudou. O número de acessos aumentou, os dados cresceram significativamente, e a forma de trabalhar com eles precisou ser repensada. Surgiu a dúvida se o modelo relacional ainda atendia à demanda.
Nesse contexto, a eventual consistência tornou-se uma possibilidade, pois a disponibilidade e a velocidade de receber e processar informações passaram a ser essenciais. Isso abriu espaço para o desenvolvimento de diversos engines de bancos de dados. Nos anos 2000, foram criados os bancos de dados NoSQL, que possuem focos especializados e são categorizados em quatro tipos: chave-valor, documentos, colunar e grafos. Atualmente, existem bancos NoSQL que tentam combinar o melhor dos dois mundos, como time series e SQL, para atender ao mercado. O crescimento da cloud impulsionou essa transformação.
Com a cloud, tudo se tornou um produto. Agora, pagamos pelo que consumimos, sem a necessidade de um datacenter próprio. Infraestrutura, plataforma, software e banco de dados se transformaram em serviços. Como pessoas engenheiras de software, precisamos saber lidar com isso, conectar-nos a esses bancos e utilizá-los da melhor maneira possível para resolver os problemas envolvidos.
Neste curso, vamos abordar essas questões na prática. Temos um projeto chamado PolyAnalyzer, que inicialmente será implementado em um banco de dados SQL. A partir daí, vamos avançar e implementar a persistência poliglota. Será uma experiência interessante, e esperamos que todos gostem.
Na próxima aula, vamos entender melhor como isso funciona. O PolyAnalyzer começou a receber requisições e a sofrer degradação, pois, quanto mais requisições, mais contenção de recursos o servidor enfrenta. Ele precisa controlar a concorrência, já que é um sistema transacional, e isso traz desafios. Vamos reconhecer esses desafios e evoluir a aplicação.
Esperamos explorar essas questões ao longo do curso e aprender cada vez mais. Até a próxima aula!
Agora, vamos explorar o projeto PolyAnalyzer e entender por que o seu ponto fraco atualmente é a utilização exclusiva do modelo relacional, tanto para escrita quanto para leitura. Vamos identificar onde podemos integrar nossos modelos de leitura, ou read models, utilizando outras tecnologias de persistência, como um banco de dados NoSQL, para alcançar a performance desejada e explorar a persistência poliglota.
O PolyAnalyzer é uma plataforma SaaS que tem como objetivo tratar feedbacks de usuários. Com base nesses feedbacks, realiza-se uma análise de sentimentos, detectando polaridade e pontuação. O processamento também inclui a classificação dos tópicos, identificando sobre o que cada tópico trata. Esses dados são agregados, armazenados no banco de dados e, em seguida, notificados no dashboard. Assim, nossos analistas e responsáveis pelo produto podem analisar e visualizar os feedbacks, utilizando tecnologia de server-side event para visualização em tempo real, além de outras informações, como IPs e agregações de dados, para auxiliar na análise do comportamento relacionado ao produto.
Para começar, vamos configurar os serviços necessários para a aplicação utilizando o Docker Compose. Isso inclui o RabbitMQ como message broker e o banco de dados MySQL para armazenamento de dados. Veja a configuração abaixo:
services:
rabbitmq:
image: rabbitmq:3.8-management
ports:
- "5672:5672"
- "15672:15672" # console
environment:
RABBITMQ_DEFAULT_USER: polyanalyzer
RABBITMQ_DEFAULT_PASS: polyanalyzer
volumes:
- rabbitmq-data:/var/lib/rabbitmq
mysql:
image: mysql:8.0
container_name: polyanalyzer-mysql
environment:
MYSQL_DATABASE: polyanalyzer
MYSQL_USER: polyanalyzer
MYSQL_PASSWORD: polyanalyzer
MYSQL_ROOT_PASSWORD: root
TZ: America/Sao_Paulo
LANG: pt_BR:pt
LANGUAGE: pt_BR:pt
LC_ALL: pt_BR.UTF-8
ports:
- "3306:3306"
command:
- "--max_connections=5000"
- "--character-set-server=utf8mb4"
- "--collation-server=utf8mb4_0900_ai_ci"
volumes:
- mysql-large-data:/var/lib/mysql
volumes:
rabbitmq-data:
driver: local
mysql-small-data:
driver: local
mysql-large-data:
driver: local
O projeto segue uma arquitetura de portas e adaptadores, ou hexagonal, que separa os modelos da infraestrutura. No modelo, focamos nas interfaces que permitem que, na infraestrutura, nos adaptadores, implementemos os serviços externos. O modelo contém a implementação das regras de negócio, contadores que trazem métricas e quantidades relacionadas aos sentimentos processados, contagem de feedbacks e tópicos, além dos resolvers, que fornecem dados de forma abrangente, como feedbacks recentes, distribuição de sentimentos ao longo do tempo e tendências de sentimentos.
Na infraestrutura, temos os adaptadores com implementações específicas para cada serviço, atualmente utilizando um banco de dados relacional, como o MySQL. Utilizamos o Docker Compose para rodar e subir todos os serviços necessários para a aplicação, incluindo o RabbitMQ como message broker e o banco de dados MySQL para armazenamento de dados.
Na parte da web, recebemos informações através de controles que processam requisições REST, utilizadas pelo nosso dashboard. Esses controles possuem todos os endpoints consumidos pelo dashboard web que a aplicação disponibiliza. Vamos ver como o FeedbackController é implementado para lidar com a criação de feedbacks:
package br.com.polyanalyzer.infrastructure.adapter.web.ingestion;
import ...
@RestController
@RequestMapping("/feedbacks")
public class FeedbackController {
private final FeedbackRegistration feedbackRegistration;
public FeedbackController(FeedbackRegistration feedbackRegistration) {
this.feedbackRegistration = feedbackRegistration;
}
@PostMapping
@Transactional
public ResponseEntity<FeedbackResponse> create(@Valid @RequestBody FeedbackRequest request) {
UUID id = feedbackRegistration.register(request.comment());
return ResponseEntity.created(URI.create("/feedbacks/" + id)).body(new FeedbackResponse(id));
}
}
O processamento começa com o envio de feedback pelo usuário, que é interceptado pelo nosso controller. Este inicializa uma transação para registrar o feedback, que é salvo no banco de dados e gera um evento chamado Feedback Received. Utilizamos o Outbox Pattern, um padrão de microserviço que garante o envio dos eventos para o message broker, mesmo em caso de problemas de infraestrutura. A mensagem fica pronta para ser enviada ao broker, e um serviço chamado Outbox Event Publisher busca os dados no banco, publica os eventos no RabbitMQ e marca o evento como salvo.
O próximo passo é o processamento de análise de sentimentos. O RabbitMQ captura a mensagem enviada pelo Outbox Event Publisher e a envia para o consumer, que inicia uma transação e envia os dados para o serviço de detecção e análise de sentimentos. Após o processamento, os dados são enviados ao banco de dados e um novo evento é gerado para o próximo passo, que é a detecção de tópicos. Ao concluir a transação, a mensagem de confirmação é enviada de volta ao broker.
Trabalhamos com envio de eventos e, nesse modelo, lidamos com consistência eventual, pois todos esses processos são executados de forma independente, não de maneira serial ou controlada.
Quando trabalhamos em modo assíncrono, estamos lidando com uma eventual consistência, mesmo utilizando um banco de dados relacional. O processo envolve o envio de dados para o broker, que utiliza o outbox pattern. O outbox event publisher captura o evento, envia-o novamente para a fila e processa o próximo consumer. Este, por sua vez, processa os tópicos, abre uma transação, detecta e salva os tópicos, e envia um evento confirmando que tudo está concluído, finalizando a transação e o processamento.
Após o processamento, o consumer envia o evento processado para o dashboard e para o controller, que aguarda um evento de domínio. Este evento indica que o processamento foi concluído e é enviado para os clientes conectados através da tecnologia de server-side event, evitando o uso de polling. No final, um acknowledgement é enviado para o broker. Não há transação envolvida nesse processo, pois não há alteração de dados, apenas notificação, o que permite uma boa escalabilidade. A comunicação ocorre apenas entre os serviços, sem impactar a infraestrutura do banco de dados.
No cenário apresentado, entendemos como todo o processo funciona. O processo ACID oferece grandes benefícios para sistemas transacionais, mas também possui suas particularidades. Não há "almoço grátis", e a transação ACID pode impactar sistemas de alta demanda, tanto para leitura quanto para escrita.
Vamos executar a aplicação para entender melhor seu funcionamento. No terminal, subimos a infraestrutura da aplicação com o comando:
docker compose up -d
E em seguida, executamos a aplicação com:
mvn spring-boot:run
Após subir a infraestrutura, podemos verificar o dashboard. Nele, já temos alguns dados persistidos dos testes realizados, como números, QRPs, feedbacks, tópicos e sentimentos, todos capturados para visualização.
Agora, vamos simular o acesso a esses dados. Primeiro, simulamos o dashboard para metrificar o que ocorre nos bastidores. Subimos a infraestrutura com 100 administradores simulando acesso ao dashboard por 2 minutos. Com o dashboard metrificado, conseguimos observar a latência das métricas, que está em torno de 426 milissegundos, o que é considerado alto. A latência de sentimentos está próxima de 1 segundo, e a dos tópicos, quase meio segundo. Mesmo sem carga, apenas com leitura de 10 administradores, já observamos esses tempos.
Vamos simular um cliente executando o processo. Em um cenário real, um produto poderia ter milhões de clientes acessando e enviando feedbacks. Para simplificar, simulamos mil clientes acessando o aplicativo e enviando feedbacks. Ao executar, observamos que o tempo de resposta aumentou para 3 segundos, e a distribuição para quase 200 milissegundos. Os QIPS globais variam, e com mil usuários, já percebemos o impacto na execução. Para envio, a aplicação atende bem, com menos de 100 milissegundos por mensagem.
O problema ocorre porque o banco de dados relacional precisa gerenciar transações, comprometendo a retenção ao controlar o acesso concorrente nas tabelas. Isso é um ponto fraco para sistemas de alta demanda. Empresas costumam ter várias réplicas de escrita e leitura de bancos de dados relacionais, separando as conexões entre os nós, pois o modelo relacional foi projetado para escala vertical, não horizontal.
Para melhorar a pesquisa de dados, podemos pensar em reproduzir e delegar a responsabilidade do modelo relacional para outro mecanismo de persistência, uma vez que o dado já está salvo. Assim, não precisamos alterar o dado constantemente. Na próxima aula, começaremos a implementar um read model e isolar esse processamento da aplicação. Nos vemos lá.
O curso Persistência Poliglota: arquiteturas e otimização de desempenho possui 308 minutos de vídeos, em um total de 60 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Matricule-se no plano PLUS e garanta:
Jornada de estudos progressiva que te guia desde os fundamentos até a atuação prática. Você acompanha sua evolução, entende os próximos passos e se aprofunda nos conteúdos com quem é referência no mercado.
Mobile, Programação, Front-end, DevOps, UX & Design, Marketing Digital, Data Science, Inovação & Gestão, Inteligência Artificial
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você participa de eventos exclusivos, pode tirar dúvidas em estudos colaborativos e ainda conta com mentorias em grupo com especialistas de diversas áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Para estudantes ultra comprometidos atingirem seu objetivo mais rápido.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Conecte-se ao mercado com mentoria individual personalizada, vagas exclusivas e networking estratégico que impulsionam sua carreira tech para o próximo nível.