Olá, pessoal

Neste artigo, voltamos aos serviços de aplicação, mostrando uma forma de implementá-los com o controle transacional, de um modo totalmente extensível.

Caso ainda não tenha feito, recomendo a leitura <deste post> – sobre a camada de aplicação – para melhor contextualizá-lo sobre onde os serviços de aplicação se encaixam em uma arquitetura em camadas.

Vale ressaltar que veremos uma solução baseada em “CommandHandlers” para lidar com serviços que executam alguma ação que altere o estado do sistema. Nenhuma solução referente às consultas da aplicação será mostrada.

Vamos lá!

TERMINOLOGIA

A terminologia utilizada no código é mais comum em aplicações que utilizam o padrão CQRS (Command Query Responsibility Segregation), porém, mesmo sem o uso de CQRS, a ideia aqui é a mesma:

  • Command: indica a ação a ser executada e que provocará alguma alteração de estado na aplicação. Tecnicamente, um command é implementado como uma estrutura de dados que carrega as informações necessárias para sua execução, de forma semelhante a um DTO (Data Transfer Object) e seu nome é dado no infinitivo ou no imperativo, como uma ordem a ser realizada: “EfetuarBaixa” ou “EfetueABaixa”.
  • CommandHandler: serviço que recebe um command, extrai seus dados e invoca ou cria algum objeto de domínio para realizar uma operação de negócio. Aqui, um command handler equivale a um “serviço de aplicação” (terminologia do DDD).

DEFININDO AS INTERFACES BÁSICAS

A primeira interface, chamada de “CommandHandler”, é a interface que todos os serviços deverão implementar. Ela define apenas 1 método (“Executar”), que recebe de parâmetro um “command”, que nada mais é do que uma estrutura que carrega os  dados necessários para a execução do comando:

public interface CommandHandler<in TCommand> where TCommand : Command
{
    void Executar(TCommand cmd);
}

public interface Command { }

A segunda interface (“Command”), embora não obrigatória, é apenas uma interface de marcação para obrigar que todo objeto passado para o método “Executar” seja do tipo “Command” (notem a constraint “where” no “CommandHandler” acima).

IMPLEMENTANDO UM SERVIÇO CONCRETO

Para implementarmos um serviço concreto, não há segredo: basta fazê-lo implementar a interface “CommandHandler”:

public class AtualizacaoDeEndereco : CommandHandler<AtualizarEndereco>
{
    private readonly RepositorioDeCliente _repositorioDeCliente;

    public AtualizacaoDeEndereco(RepositorioDeCliente repositorioDeCliente)
    {
        _repositorioDeCliente = repositorioDeCliente;
    }

    public void Executar(AtualizarEndereco cmd)
    {
        var cliente = _repositorioDeCliente.ObterPor(cmd.IdDoCliente);
        var novoEndereco = new Endereco(cmd.Logradouro, cmd.Bairro, cmd.Cep);
        cliente.AtualizarEndereco(novoEndereco);
    }
}

public class AtualizarEndereco : Command
{
    public int IdDoCliente { get; private set; }
    public string Logradouro { get; private set; }
    public string Bairro { get; private set; }
    public string Cep { get; private set; }

    public AtualizarEndereco(int idDoCliente, string logradouro, string bairro, string cep)
    {
        IdDoCliente = idDoCliente;
        Logradouro = logradouro;
        Bairro = bairro;
        Cep = cep;
    }
}

Vejam que, devido à restrição do “CommandHandler”, fizemos o command implementar a interface “Command”.

IMPLEMENTANDO UM DECORATOR CONCRETO

Para incluir a capacidade transacional em nossos serviços, não alteraremos nenhum serviço concreto. Todos permanecerão com a estrutura semelhante ao exemplo anterior.

Ao invés disso, faremos o tratamento de transações usando o design pattern decorator: criamos uma nova classe que implementa “CommandHandler” e recebe um “CommandHandler” em seu construtor.

“TransactionalCommandHandler” é um decorator porque ele “decora” o command handler recebido de parâmetro, ou seja, acrescenta comportamento ao command handler original. Isso é visto em seu método “Executar” abaixo:

public class TransactionalCommandHandler<TCommand> : CommandHandler<TCommand>
       where TCommand : Command
{
    private readonly Transactional _transactional;
    private readonly CommandHandler _commandHandler;

    public TransactionalCommandHandler(Transactional transactional,
           CommandHandler<TCommand> commandHandler)
    {
        _transactional = transactional;
        _commandHandler = commandHandler;
    }

    public void Executar(TCommand cmd)
    {
        _transactional.BeginTransaction();

        try
        {
            _commandHandler.Executar(cmd);
            _transactional.Commit();
        }
        catch
        {
            _transactional.Rollback();
            throw;
        }
    }
}

Notem que o método “Executar” do decorator, além de chamar o “Executar” do command handler original (linha 20), realiza todo o controle transacional no código ao redor.

Algumas vantagens do design:

  • Ao utilizar um decorator para realizar o controle transacional, removendo essa responsabilidade dos serviços concretos, garantimos nestes, que o Princípio da Responsabilidade Única seja atendido, uma vez que eles se preocupam apenas com a lógica da aplicação.
  • O uso do decorator também garante a possibilidade de estender o comportamento de uma classe (acrescentado o controle de transação) sem que tenhamos que alterá-la (lembram do Princípio do Aberto/Fechado? ).
  • O design permite que os serviços concretos sejam testados sem qualquer conhecimento a respeito de transações.
  • O decorator “TransactionalCommandHandler” é totalmente testável, uma vez que ele não depende de classes concretas; ao contrário, ele recebe abstrações do command handler decorado e de um controlador de transações (interface Transactional) via construtor.

A INTERFACE “TRANSACTIONAL”

Usar a interface Transactional – ao invés de depender diretamente de alguma classe/lib responsável pelo controle transacional – é uma decisão arquitetural de não fazer o core da aplicação depender de detalhes técnicos, o que segue o Princípio da Inversão de Dependência.

Um exemplo de implementação dessa interface – que ficaria na camada de infraestrutura – utilizando o NHibernate, poderia ser:

public class NHibernateTransaction : Transactional
{
    private readonly ISession _session;

    public NHibernateTransaction(ISession session)
    {
        _session = session;
    }

    public void BeginTransaction()
    {
        _session.BeginTransaction();
    }

    public void Commit()
    {
        _session.Transaction.Commit();
    }

    public void Rollback()
    {
        _session.Transaction.Rollback();
    }
}

INJETANDO O SERVIÇO “DECORADO”

Quase tudo pronto. Agora para usarmos um serviço com capacidade transacional, basta decorá-lo assim:

.........
var alteracaoDeEndereco = new AlteracaoDeEndereco(/*dependencias*/);
var alteracaoDeEnderecoTransacional = new TransactionalCommandHandler(_transactional, alteracaoDeEndereco);
alteracaoDeEnderecoTransacional.Executar(new AlterarEndereco(/*dados do cmd*/));

Esperem. O código consumidor do serviço depende diretamente do decorator concreto e do serviço concreto!! Isso nos obriga a fornecer instâncias para todas as dependências deles, como “_transactional” e qualquer outra dependência exigida por “AlteracaoDeEndereco”.

Uma solução mais adequada seria injetar o serviço desejado onde formos utilizá-lo:

public class ClientesController : Controller
{
    private readonly CommandHandler<AlterarEndereco> _alteracaoDeEndereco;

    public ClientesController(CommandHandler<AlterarEndereco> alteracaoDeEndereco)
    {
        _alteracaoDeEndereco = alteracaoDeEndereco;
    }

    public ActionResult AlterarEndereco()
    {
        _alteracaoDeEndereco.Executar(new AlterarEndereco(/*dados do cmd*/);
    }
}

Mas esperem (de novo!). Como fazer com que o serviço concreto a ser injetado – no caso, “AlteracaoDeEndereco” – seja decorado em tempo de execução pelo “TransactionalCommandHandler”?

Isso é simples. Basta ensinarmos essa regra ao container de DI. Eu tenho trabalhado com o SimpleInjector, que é bem simples mesmo, muito bem documentado e que fornece o que eu preciso nesta situação de forma bem prática:

// NO START DA APLICAÇÃO:
// criando o container (SimpleInjector)
var container = new Container();

// registrando todos os servicos concretos que implementam CommandHandler
container.Register(typeof(CommandHandler<>),
                   new[] { typeof(CommandHandler<>).Assembly });

// registrando o decorator para todo CommandHandler
container.RegisterDecorator(typeof(CommandHandler<>),
                            typeof(TransactionalCommandHandler<>));

Prontinho! Agora todo CommandHandler<T>, ao ser injetado, será automaticamente decorado em tempo de execução! (Isso é considerado uma forma de interception e podemos utilizar decorators “aninhados” para atendermos outros cross-cutting concerns, como logging.)

ENCERRANDO…

Neste artigo, vimos como implementar um serviço de aplicação que executa alguma ação de alteração de estado – aqui chamado de command handler – usando o design pattern decorator.

Obviamente, esta não é a solução única mas é uma das que permite grande flexibilidade. Você pode optar por uma solução onde haja um CommandHandlerBase com a capacidade transacional (herança = maior acoplamento) ou ainda utilizar os recursos de interception do seu container de DI (veja este e outros cenários avançados na documentação do SimpleInjector).

Percebam também que a solução mostrada não lida com os níveis de isolamento das transações, o que pode ser atendido facilmente, por exemplo, criando-se uma hierarquia de decorators transacionais e configurando o container explicitamente para decorar o serviço que necessite de um nível de isolamento diferente com o decorator correspondente (assunto para um próximo post, talvez?).

Comentem aí…o que acharam da solução? Como vocês tem projetado os commands em suas aplicações?

Era isso!