Implementando serviços de aplicação transacionais

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!

Anúncios

9 comentários em “Implementando serviços de aplicação transacionais

  1. Parabéns pelo post, vc disponibilizou os fontes? Fiquei como uma dúvida o quanto a utilização do IN na inteface ()?

    1. Obrigado, David
      Os fontes estão neste projeto do github: https://github.com/robsoncastilho/NCleanArchitecture.

      Quanto ao ‘IN’, é uma característica dos generics chamada de contravariancia.
      Ela permite que, por exemplo, onde eu tenha: AlgumMetodo(CommandHandler<Comando> cmd), eu possa passar de parâmetro alguém que implemente CommandHandler<Comando> OU alguém que implemente CommandHandler<OutroComando>, onde OutroComando é um tipo acima de Comando em uma hierarquia de classes.
      Para saber mais: https://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx

      No entanto, para a solução fornecida no artigo, esse recurso não é obrigatório, ou seja, o parâmetro TCommand não precisa ser contravariante pois não é intenção – até o momento – usar este recurso como no exemplo do método mais acima.

      []s

  2. Robson, belo post! Agora uma dúvida bem besta… Como seria para você a implementação do método atualizar endereço da classe Cliente? Pelo que percebi trata se de uma entidade do domínio. Você injetaria nela um domain service ou o repositório para “fazer” o update?

    1. Obrigado, Bruno.

      Como seria para você a implementação do método atualizar endereço da classe Cliente?
      R.: A implementação seria uma mera atribuição do novo endereço no objeto cliente.

      Você injetaria nela um domain service ou o repositório para “fazer” o update?
      R.: Nenhum dos dois.
      Um DOMAIN service é uma operação do domínio, ie, uma operação DE NEGÓCIO e não tem a ver com atualizações de banco de dados.
      Também não gosto de deixar um objeto de domínio saber se persistir, o que viola o princípio da responsabilidade única.
      Sendo assim, a opção seria um segundo objeto cuidar da persistência, o qual poderia ser sim o repositório. O modo mais simples seria chama-lo logo após a operação de negócio (cliente.AtualizarEndereco()): repositorioDeCliente.Atualizar(cliente). No entanto, revendo o exemplo do artigo, essa atualização é implícita, pois estou supondo o uso de um ORM, o qual sabe marcar o objeto como alterado. Sendo assim, no Commit() da transação, o “update” no banco será realizado.

      Concluindo, o serviço de aplicação é que orquestra tudo, inclusive a chamada ao repositório. Não injete repositórios nos seus objetos de domínio, pois acabarão mais poluídos, mais difíceis de testar, além de provavelmente, ser um indício de que ele tá querendo fazer muita coisa (que poderia estar em um domain service, por ex.).

      É isso!
      []s

  3. Robson, no tópico INJETANDO O SERVIÇO “DECORADO” a última linha de código deveria ser alteracaoDeEnderecoTransacional.Executar(new AlterarEndereco(/*dados do cmd*/)); em vez de alteracaoDeEndereco.Executar(new AlterarEndereco(/*dados do cmd*/));

  4. Robson, pq vc não usou o prefixo I nos nomes das interfaces? Eu não gosto desse prefixo, mas acabo usando pq faz farte das convenções do C#.

    1. Olá, Edno
      É uma convenção (não usar o ‘I’) que te força a pensar melhor no nome da implementação concreta, além de ficar mais limpo.
      Eu ainda uso o ‘I’, mas tenho eliminado quando posso. Apesar de convenção da plataforma, é um resquício de notação húngara.
      []s!

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s