🚀 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.

Muito obrigado! Acabei não focando muito no benchmarking no post e nem no vídeo devido ao fato de que o SQLite também é bem rápido rsrs, então a diferença acaba sendo pequena e por vezes o SQLite acaba sendo mais rápido que o Redis, ainda mais com ambos rodando localmente. Porém, tem alguns vídeos no youtube (dos quais peguei uma base para fazer esse projeto) que fazem essa comparação, vou deixar o link deles abaixo: - [Vídeo da Rocketseat (Postgres + Redis)](https://www.youtube.com/watch?v=hf3EHCXsRYM) - [Vídeo do Tadeu Rangel (API do Spotify + Redis)](https://www.youtube.com/watch?v=RTJ7u4QE9UE) Sobre o pattern Observer, achei bem interessante a aplicação que você fez dele. Teria algum repositório com essa implementação que você possa disponibilizar? Fiquei bem curioso, até porque esse assunto de pub/sub foi algo que estava estudando recentemente e curti muito (inclusive pretendo fazer um vídeo sobre ele também, e provavelmente um post aqui).
Aqui tem um exemplo, uma vez que tentei implementar um Mutex entre duas threads em Python, utilizando o Redis https://github.com/gitandlucsil/RedisImplementsMutex
Valeu mano, vou dar uma olhada!
Tranquilo, qualquer dúvida me chama ai.
Muito bom o projeto!

Conteúdo brabo, parabéns

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.