Decorator Pattern: Implementando Cache no .NET
Recentemente fiz uma apresentação sobre o uso de Design Patterns na prática. O objetivo dessa apresentação era demonstrar alguns padrões resolvendo problemas de sistemas reais em alinhamento com os princípios do SOLID.
Neste artigo trago de forma mais detalhada como usei o Decorator Pattern para resolver a necessidade implementar cache na camada de serviço.
Obs.: Esta prática funciona perfeitamente para qualquer camada da sua aplicação, desde que o princípio “Princípio da inversão da dependência” esteja sendo atendido.
Este artigo não entra em detalhes sobre o que é o SOLID ou como funciona uma solução de cache, vamos focar apenas na sua implementação utilizando o Decorator Pattern.
O Problema
Estamos fazendo uso de um serviço que é lento e não sofre mudanças com a mesma frequência em que é chamado, desta forma precisamos otimizar as chamadas a este serviço.
Alternativa 1: Realizar a implementação do cache dentro do serviço, dando ao serviço a responsabilidade gerenciar o uso do Cache.
Alternativa 2: Realizar a implementação do cache em quem chama o serviço, neste caso dando ao controller tal responsabilidade.
Alternativa 3: Implementar o uso do cache de forma isolada, não modificando o funcionamento do serviço, muito menos de quem o chama.
Como vocês devem ter imaginado, as duas primeiras alternativas ferem os princípios “Princípio da responsabilidade única” e “Princípio Aberto-Fechado”
A Solução
Nossa proposta para resolver esse problema é criar duas implementações para a interface do serviço e aplicar o Decorator Pattern para gerar o seguinte fluxo.
A solução consiste em criar uma implementação da interface IDetranVerificadorDebitosService, esta nova implementação fica responsável por gerenciar o cache através da interface IDistributedCache e chamar o _Inner apenas quando a resposta não estiver persistida em cache.
Neste modelo o controller continua dependendo de um IDetranVerificadorDebitosService, porém faremos a injeção de um novo Decorate, tornando a gestão do cache transparente tanto para o Controller quanto para o próprio serviço.
A interface IDistrubutedCache abstrai para o nosso código qual o mecanismo está sendo utilizado para fazer a gestão do cache, o .NET possui algumas implementações prontas para uso bastando apenas indicar qual implementação deve ser usada no Startup
No exemplo abaixo temos a uma implementação fake (também nativa do .NET) comentada e uma segunda implementação usando REDIS, modificar o provider de cache que você gostaria de usar é tão simples quanto isso.
Obs.: Esta configuração deve ser utilizada apenas para ambientes de desenvolvimento, para uso produtivo é recomendado estudar a documentação do REDIS.
Ao colocar a nossa aplicação para rodar e executar várias requisições GET https://localhost:5001/api/Detran/Debitos?Placa=ABC1234&UF=PE podemos notar que apenas a primeira requisição levou o tempo máximo de 5 segundos, as demais tiveram o tempo de execução reduzido por fazer uso do cache.
Passados os 20 segundos que foram definidos como TTL para o nosso cache, ele é desprezado e uma nova requisição é feita ao serviço.
Segue link do repositório completo no GitHub com este e outros exemplos implementados. https://github.com/fructuoso/DesignPatternSamples
Referências: