cross-cutting concern com c# e Func

Tempo estimado de leitura: Não sei o quão rápido você é capaz de ler.

Olá, tudo bem?

Recentemente tive que implementar algumas novas funcionalidades em uma loja virtual de um site aqui na Nova Zelândia.

Em diversas classes, haviam chamadas hard-coded para gravar em log objetos de request / response trafegados entre a aplicação e uma camada de serviços. Algo mais ou menos assim:

public class ServicoFoo : IServicoFoo
{
    private readonly IFoo _foo;

    public ServicoFoo(IFoo foo)
    {
        _foo = foo;
    }

    public FooResponse CallFoo(FooRequest request)
    {
        Logger.Log("CallFoo - Request", DateTime.Now, request);

        var response = _foo.CallFoo(request);

        Logger.Log("CallFoo - Response", DateTime.Now, response);

        return response;
    }
}

O fato é que as classes e os métodos são bem mais complexos que este exemplo, onde as vezes fazemos 3 ou mais chamadas para a camada de serviços.

Tirando o fato de “sujar” regras de negócio com esses logs, se amanhã ou depois alguém resolver mudar a assinatura do método Log, centenas de classes e métodos serão afetados. Obviamente, o Visual Studio oferece recursos para efetuar a refatoração de maneira mais simples, mas o que realmente me incomodava era ter que escrever as mesmas linhas para logar o request / response a cada novo serviço que eu viera a chamar, além de não ter nada a ver com a regra de negócio (como já disse anteriormente).

Um jeito mais elegante de resolver este problema seria utilizar algum framework para AOP / implementar o design pattern decorator. Eu quis resolver de um jeito mais fácil, e como nos últimos tempos tenho investido no estudo de programação funcional, isto vem influenciando minha maneira de pensar e escrever código. Sendo assim tive a seguinte ideia:

public class ServiceInvoker
{
    private static void LogRequest<T>(T obj, string methodBeingInvoked, 
            bool isResponse = false)
    {
        var direction = isResponse ? " - Response" : " - Request";

        Logger.Log(methodBeingInvoked + direction, DateTime.Now, obj);
    }

    public static TResponse Invoke<TRequest, TResponse>(Func<TRequest, TResponse> service, 
                    TRequest request, string method)
    {
        LogRequest<TRequest>(request, method);

        TResponse response = service(request);

        LogRequest<TResponse>(response, method, isResponse: true);

        return response;
    }
}

e minhas classes de serviço agora são assim:

public class ServicoFoo : IServicoFoo
{
    private readonly IFoo _foo;

    public ServicoFoo(IFoo foo)
    {
        _foo = foo;
    }

    public FooResponse CallFoo(FooRequest request)
    {
        var method = "CallFoo";
        return ServiceInvoker.Invoke<FooRequest, FooResponse>
            ( r => foo.CallFoo(request), request, method);
    }
}

Com certeza existem outras maneiras de resolver este problema usando a mesma idéia, mas o importante é que resolveu meu problema e não tenho mais centenas de linhas repetidas e espalhadas por diversas classes.
Tem um post legal do Elemar que de certa maneira está relacionado com a idéia deste post:

Garantindo que recursos sejam liberados (em Java e em C#)

Por hoje era isto. Até o próximo post pessoal!

Compartilhar