🚀 Implementando cache em uma API utilizando Design Pattern (Redis + Proxy)
Fala pessoal, tudo blz? Hoje venho compartilhar um pouco do que venho estudando ultimamente. Trata-se de uma implementação simples de um sistema de cache no backend utilizando o banco Redis, seguindo a estrutura do design pattern Proxy.
Antes de apresentar o código do projeto, vamos entender um pouco acerca das tecnologias que foram utilizadas.
O que é cache
Cache, ou memória cache, trata-se de um armazenamento de acesso rápido utilizado por computadores e smartphones para guardar arquivos temporários que são acessados com frequência.
Caso deseje entender melhor sobre esse assunto, acesse o post onde explico de forma mais detalhada e com analogias o funcionamento do cache.
Utilizando cache no backend com o Redis
Utilizamos a estratégia de cache para aumentar a performance das requisições da nossa aplicação. Iremos utilizar o Redis (banco de dados open-source) como memória cache nesse exemplo devido a sua rápida resposta a requisições de leitura.
O padrão proxy
Na implementação do cache, iremos utilizar o design pattern Proxy. Isso permitirá que o Redis atue como um intermediário entre o código e o banco de dados principal da aplicação (nesse exemplo, para fins de práticidade, será o SQLite junto com o Prisma). Dessa forma, antes de solicitarmos algum dado ao banco, iremos checar se esse mesmo dado se encontra em cache, ou seja, salvo no Redis.
Caso deseje ver mais detalhes sobre o padrão Proxy, recomendo assistir o vídeo, que fiz sobre o assunto e também dar uma lida na página do Proxy, no site do Refactoring Guru.
Aplicando na prática
Não irei mostrar o código completo aqui, apenas a classes principais que implementam a estratégia de cache. Caso deseje ver o código-fonte completo (com algumas modificações), acesse o repositório da aplicação, ou então veja o vídeo que fiz desenvolvendo esse projeto.
- Repository principal (SQLite)
export class PrismaUserRepository implements UserRepository {
constructor(
private readonly prisma: PrismaService
) {}
async findMany() {
const users = await this.prisma.user.findMany()
return users;
}
}
- Implementação do Proxy (Redis como intermediário entre código e SQLite)
export class RedisUserRepository implements UserRepository {
constructor(
private readonly redis: RedisService,
private readonly prismaUserRepository: PrismaUserRepository,
) {}
async findMany() {
// Verificar se os dados já estão em cache
const cachedUsers = await this.redis.get('users');
// Caso não estejam, buscar os dados no banco principal
// e em seguida salvá-los no cache
if (!cachedUsers) {
const users = await this.prismaUserRepository.findMany();
// Salvando no cache com expiração de 15 segundos
await this.redis.set('users', JSON.stringify(users), 'EX', 15);
return users;
}
// Caso estejam, retornar os dados em cache
return JSON.parse(cachedUsers);
}
}
Espero que tenha curtido! Fique a vontade para deixar seu feedback!
Referências
Obrigado, estava procurando uma forma de fazer cache de memória. Parece ser bem simples de implementar né, ou é impressão minha? Vou testar logo logo no meu servidor.
Link do repositório se quiser acompanhar: https://github.com/pedigru3/shoplanner-server
Parabéns pelo trabalho, muito interessante! Por um acaso, teria como fazer um benchmarking/comparacao de velocidade e desempenho recuperando a informacao do Redis e direto do banco de dados? Seria um comparativo legal de se fazer.
E entrando no assunto Design Patterns, já utilizei muito o Redis para implementar o design patterna Observable, utilizando as propriedade publish/subscribe do Redis.
Boa! acredito que também vale a pena implementar algo que caso a consulta no banco de dados retorne um erro, isso não seja salvo no cache.