.NET - Aplicando o Cross Cutting


Hoje veremos o que é o Cross Cutting e como podemos implementá-lo na plataforma .NET.

O Cross Cutting ou responsabilidades transversais são métodos comuns fornecidos por uma camada ou serviço específico usado por todas as camadas de uma aplicação. Seria como uma camada/serviço que cruza toda a hierarquia.

Como exemplo de tarefas que podemos implementar usando o cross cutting temos:

Neste primeiro contato vamos apresentar o conceito e mostrar dois exemplos de como implementar o cross cutting em um aplicativo .NET onde em cada exemplo veremos os prós e os contras.

Adicionando código para consumir classes

Quando o cross cutting é implementado simplesmente adicionando código nas classes, o código pode ser poluído com dependências que serão um fardo quando o código estiver em teste ou o desenvolvimento paralelo for feito pela equipe de desenvolvimento.

Tomemos o exemplo de código a seguir:

public class ProdutoRepository    
{
 public void Update(string produtoNome)
 {
     Update(produtoNome);
     new EventLog("MeuLog").WriteEntry(string.Format("{0} foi atualizado no banco de dados"));
 }
}

O código acima vai funcionar, mas ele apresenta as seguintes desvantagens :

- Ele viola o princípio de responsabilidade única com uma atualização e uma funcionalidade de registro de log;
- O método de atualização não pode ser testado de maneira isolada porque o método de atualização não pode ser chamado sem acionar o serviço de registro;
- O logger (classe EventLog) não pode ser substituído por um stub ou logger falso de uma maneira fácil;
- A classe ProdutoRepository está arrastando uma dependência da DLL do logger, o que significa que sempre que houver algo errado com a base de código do logger, há algo errado com o ProdutoRepository.

Como podemos melhorar isso ? 

Vejamos a seguir duas possibilidades...

Usando a injeção de dependência

Usar a injeção de dependência é uma solução melhor, mas ainda precisa ser feita em cada classe e pode, portanto, ser um processo bastante repetitivo.

Além disso, a interface apresenta agora um comportamento não específico da classe, por exemplo, o registro é um comportamento não intrínseco do ProdutoRepository, mas é (também) intrusivo presente na interface.

public class ProdutoRepository
{
        ILogger _logger; 
        public ProdutoRepository(ILogger logger)
        {
            _logger = logger;
        } 
        public void Update(string produtoNome)
        {
            Update(produtoNome);
            logger.WriteEntry(string.Format("{0} foi atualizado no banco de dados"));
        }
}

Usando o padrão Decorator

O padrão Decorator é uma das maneiras mais óbvias de obedecer aos princípios de programação SOLID. Uma boa implementação demonstrará todos os princípios.

Um decorador atua como um wrapper em torno de uma classe por meio de consumir essa classe e, simultaneamente, implementar a mesma interface dessa classe.

Isso torna possível substituir a classe original e sobrescrever os métodos dessa classe. As substituições podem delegar a chamada à classe injetada, além de adicionar funcionalidades extras próprias como, por exemplo, registro ou armazenamento em cache.

public interface IProdutoRepository
{
    void Update(string produtonome);
}
public class ProdutoRepository : IProdutoRepository
{
  public void Update(string produtonome)
  {
     // salva no banco de dados
  }
}
public class ProdutoRepositoryLog : IProdutoRepository
{
        private IProdutoRepository _produtoRepository;
        public ProdutoRepositoryLog(IProdutRepository produtoRepository)
        {
              _produtoRepository = produtoRepository;
        }

        public void Update(string produtoNome)
        {
            //Delegate para o método update original
            _produtoRepository.Update(produtoNome);

            //Decoration com o recurso logging
            new EventLog("MeuLog").WriteEntry(string.Format("{0} foi atualizado no banco de dados"));
        }
 }

Vantagens e desvantagens dessa abordagem:

1- Permite aplicar os princípios SOLID com todas as vantagens transmitidas: código mais legível, testável, extensível, de fácil manutenção e fracamente acoplado.

2- Para projetos maiores, o código pode se tornar prolixo e repetitivo o que viola o princípio DRY - Don´t Repeat Yourself.

Vimos assim duas abordagens no tratamento do cross cutting. Existem outras abordagens mais complexas que podemos usar que não foram apresentadas neste artigo.

E estamos conversados...

"E percorria Jesus todas as cidades e aldeias, ensinando nas sinagogas deles, e pregando o evangelho do reino, e curando todas as enfermidades e moléstias entre o povo."
Mateus 9:35

Referências:


José Carlos Macoratti