Validação de Comandos

No artigo/vídeo anterior eu comentei como podemos expor comandos através do protocolo HTTP. O comando é serializado em JSON e enviado para o servidor que tenta encontrar a respectiva classe que representa o comando e, consequentemente, executa o seu respectivo handler para tratar a requisição. E como sabemos, os comandos são classes extremamente simples, contendo apenas poucas propriedades que parametrizam a tarefa a ser executada.

Só que os comandos podem chegar até o serviço sem as informações mínimas para a sua execução. Apesar de estarmos falando em comunicação entre sistemas, isso se aproxima muito do que temos em uma aplicação com interface com o usuário: apesar de termos uma validação sendo realizada no formulário, é uma boa prática reaplicar as validações quando ele é postado, já que do lado do cliente, a validação pode ser desabilitada, como é o caso de aplicações Web, que recorrem à JavaScript, que com algum esforço, podemos inativar no navegador e postar a mensagem sem devidas validações. A validação do lado do cliente serve para dar uma melhor experiência para o usuário, dando o feedback se alguma informação estiver inválida antes da requisição partir.

Isso é muitas vezes chamadas de validação superficial, e ela não substitui aquela que é feita pelo domínio, que por sua vez, é a qual garante a consistência da aplicação. Essa validação nos comandos devem garantir os parâmetros obrigatórios, que itens não sejam nulos ou iguais a zero, formatação de campos (e-mail, URLs, etc.), etc.

Agora, se a ideia é expor os comandos através de HTTP, temos também que tentar integrar as validações com os recursos que o protocolo fornece. Só que antes disso, precisamos incorporar nas classes dos comandos a estrutura da validação. Aqui existem diversas formas de se fazer, onde a mais comum, é declarar na classe base que representa o comando um método virtual que pode ser sobrescrito nas derivadas para customizar a validação de cada um deles.

public abstract class Command
{
    public virtual bool Validate() { }
}

A implementação do método pode ser feito de diversas formas, e uma delas sendo a forma “manual”, utilizando apenas recursos (tipos) da linguagem e criando classes que representam os erros. O método Validar retorna apenas um indicativo (bool) se a validação sucedeu ou não. Mas o que falhou? E vamos optar por retornar apenas quando a primeira falha for encontrada ou validaremos todo o objeto e retornaremos todos os problemas encontrados? Neste caso, ao invés do método retornar apenas um bool, teremos que customizar o tipo de retorno, com uma classe que totaliza todos os problemas encontrados.

public abstract class Command
{
    public virtual ValidationResult Validate()
    {
        return ValidationResult.Empty;
    }
}

public class ValidationResult
{
    public static ValidationResult Empty = new ValidationResult();
    private readonly IList<Error> errors = new List<Error>();

    public void Add(Error error) => this.errors.Add(error);

    public bool IsValid
    {
        get
        {
            return !this.errors.Any();
        }
    }

    public IEnumerable<Error> Errors
    {
        get
        {
            return this.errors;
        }
    }

    public class Error
    {
        public string Message { get; set; }
    }
}

A outra forma seria recorrer à algum framework de validação, como o FluentValidation, que já fornece diversos recursos para facilitar a validação. Depois disso, precisamos de alguma forma integrar com o HTTP, e a opção que temos é retornar o erro 400 (BadRequest), que serve justamente para representar alguma falha encontrada na postagem da requisição (onde o seu conteúdo não é válido). A resposta deve conter os dados da validação, para que o cliente consiga entender o problema e corrigir para as futuras requisições.

Por fim, ao receber a requisição, encontrar o respectivo comando e deserializar a mensagem, invocamos o método Validate e avaliamos a propriedade IsValid. Caso seja negativo, então configuramos o código de retorno do HTTP para 400 (BadRequest) e serializamos o resultado da validação  em JSON para ser devolvido ao cliente; caso o resultado da validação seja positivo, então a requisição é encaminhada para ser normalmente processada. O código na integra está disponível neste endereço.

var command = context.Request.ReadAsCommand(bus.Handlers[action]);
var validateResult = command.Validate();

if (!validateResult.IsValid)
{
    context.Response.StatusCode = 400;
    await context.Response.WriteAsync(validateResult.ToJson());
    return;
}

await bus.Send(command);

É importante dizer que isso que falamos aqui é uma validação superficial, e não deve ser utilizada sozinha, é só um complemento. O comando essencialmente não retorna resultado, mas a validação dele é algo que ocorre antes de sua execução. Agora se algo der errado durante a execução dele (pois está “tocando” o domínio, que pode estar protegido com o disparo de exceções), é muito provável que seja alguma regra de negócio que tenha sido violada, e para isso, há outras estratégias de notificação para reportar ao usuário ou à outras aplicações destas falhas, como o uso de eventos.

Publicidade

Repositórios Plug-and-Play

No artigo anterior falamos sobre o uso de JOINs para extrair as informações que armazenamos em uma base de dados relacional. Comentamos também das dificuldades que temos quando precisamos manter os dados originais do momento da execução daquela tarefa (exemplo: o nome do cliente no momento da emissão da nota fiscal). No fim deste mesmo artigo, falamos em segregação da base de dados, a fim de ter estruturas distintas e otimizadas para leitura e gravação.

Dando continuidade no artigo, vamos falar sobre a base de dados de escrita. Quando trabalhamos com um domínio rico, baseado em DDD, é comum nossas classes possuírem diversas propriedades, que representam suas características, bem como funcionalidades e comportamentos que refletem, em geral, as mesmas funções do mundo real. Uma vez que este domínio está desenvolvido, chega o momento de decidir como vamos persisti-lo fisicamente. E a resposta na maioria das vezes, a resposta sempre é o uso de uma base de dados relacional, tais como: SQL Server, Oracle, MySQL, etc.

O problema entre o nosso domínio e uma base de dados relacional é a impedância. Isso quer dizer, o atrito que temos ao mapear classes e propriedades para tabelas, colunas e linhas. Trata-se de um trabalho árduo, e muitas vezes recorremos à mapeadores (ORM) para auxiliar nesta atividade, e que apesar de fazerem um grande trabalho por convenção, há muito o que se customizar em um ambiente mais complexo. Isso sem falar sobre evolução do domínio, onde a criação de novos tipos, adição e remoção de novas propriedades que precisam ser armazenadas, refletirá no mapeamento e que deverá ser ajustado.

Por essa flexibilidade na evolução e diversos outros pontos positivos, é que bases NoSQL estão cada vez mais ganhando espaço. E aqui, vamos tentar fazer a migração de uma aplicação que faz uso de NHibernate para RavenDB. Como estamos trabalhando orientado ao domínio, nós já temos interfaces em nosso código que definem a estrutura dos repositórios que a mesma utiliza. Por padrão, temos apenas a implementação para o NHibernate:

public class RepositorioDeClientes : IRepositorioDeClientes
{
    private readonly ISession session;

    public RepositorioDeClientes(ISession session)
    {
        this.session = session;
    }

    public void Salvar(Cliente entidade)
    {
        session.Save(entidade);
    }

    public Cliente BuscarPor(string cnpj)
    {
        return
            (
                from c in session.Query<Cliente>()
                where c.Empresa.Cnpj == cnpj
                select c
            ).SingleOrDefault();
    }
}

Só que o repositório ainda depende do arquivo de mapeamento (HBM) para fazer o de/para das classes para as tabelas. Vou omitir o mapeamento aqui. Ele estará disponível no código fonte do projeto que estará relacionado à este artigo. Como a interface define a estrutura, criamos uma implementação do repositório para o RavenDB. É possível notar que a API do ORM é bem semelhante à API do RavenDB.

public class RepositorioDeClientes : IRepositorioDeClientes
{
    private readonly IDocumentSession session;

    public RepositorioDeClientes(IDocumentSession session)
    {
        this.session = session;
    }

    public void Salvar(Cliente entidade)
    {
        session.Store(entidade);
        session.SaveChanges();
    }

    public Cliente BuscarPor(string cnpj)
    {
        return
            (
                from c in session.Query<Cliente>()
                where c.Empresa.Cnpj == cnpj
                select c
            ).SingleOrDefault();
    }
}

Se notarmos os códigos que criam um novo cliente utilizando os repositórios criados acima, eles serão idênticos, exceto pela criação, que cada um deve recorrer ao seu próprio modelo de construção e conexão. Se você injeta o repositório em outras classes, elas continuarão funcionando de forma transparente, já que toda a “complexidade” está embutida no repositório, e sua interface continua expondo os mesmos métodos.

public static class Repositorios
{
    public static void Executar()
    {
        ViaNHibernate();
        ViaRavenDB();
    }

    private static void ViaNHibernate()
    {
        using (var session = NHibernateContext.Factory.OpenSession())
        {
            var repositorio = new R.NHibernate.RepositorioDeClientes(session);
            var cliente = new Cliente(new Empresa() { RazaoSocial = "Nome Ltda", Cnpj = "123" });

            repositorio.Salvar(cliente);

            Console.WriteLine(repositorio.BuscarPor(cliente.Empresa.Cnpj).Empresa.RazaoSocial);
        }
    }

    private static void ViaRavenDB()
    {
        using (var session = RavenDBContext.Store.OpenSession())
        {
            var repositorio = new R.RavenDB.RepositorioDeClientes(session);
            var cliente = new Cliente(new Empresa() { RazaoSocial = "Nome Ltda", Cnpj = "123" });

            repositorio.Salvar(cliente);

            Console.WriteLine(repositorio.BuscarPor(cliente.Empresa.Cnpj).Empresa.RazaoSocial);
        }
    }
}

E como uma das principais características de bases NoSQL é não ter um schema predefinido, não há nada que se precise fazer para estruturar a base antes de receber os dados. E com isso, todos os arquivos HBM de mapeamento do NHibernate (ou se usar o Fluent NHibernate), podem ser completamente descartados. O método BuscarPor está sendo chamado aqui apenas para gerar o round-trip de testes.

Relacionamentos

Quando trabalhamos com base de dados relacional, utilizamos os relacionamentos para referenciar em uma linha o conteúdo de outra, ou seja, para complementar a informação, e evitando com isso, a redundância de dados. Para dar um exemplo, quando um pedido é realizado, ao invés de armazenar nos itens do mesmo toda a informação acerca do produto comprado, simplesmente referenciamos o produto através de seus Id como uma chave estrangeira, e através de JOINs, conseguimos remontar o pedido e saber quais produtos foram comprados.

Como mencionei no artigo anterior, além do custo de executar um JOIN, muitas vezes a redundância é benéfica. E vale ressaltar novamente aqui: o custo de armazenamento, via de regra, é mais barato que o custo de processamento. Ao mudar para uma base de dados NoSQL, os relacionamentos são tratados de forma um pouco diferente aos quais conhecemos no mundo relacional. Abaixo vou mostrar alguns experimentos, e entender o comportamento de cada um deles para podemos escolher o qual melhor se encaixa com a nossa necessidade.

Para o exemplo, vamos considerar um exemplo bastante trivial: uma classe chamada Pedido, que possui os itens que foram comprados. Para representar cada item, teremos a classe Item, que, em princípio, referenciará o produto (classe Produto) que foi adquirido. Essa classe irá ser modificada para exemplificar cada um dos cenários. Por fim, a classe Pedido irá expor uma propriedade chamada Itens que retorna todos os itens associados à ele.

Flat

A primeira opção é conhecida como flat, ou seja, as referências serão armazenadas como parte do documento principal, embutindo todas as propriedades e seus respectivos valores. Para este primeiro cenário, a estrutura da classe Item do pedido será a seguinte:

public class Item
{
    public Item(Produto produto, int quantidade)
    {
        this.Produto = produto;
        this.Quantidade = quantidade;
        this.Total = produto.Valor * quantidade;
    }

    public Produto Produto { get; private set; }

    public int Quantidade { get; private set; }

    public decimal Total { get; private set; }
}

Note que a instância da classe produto é passada como parâmetro no construtor da classe Item e armazenado em uma propriedade. O RavenDB irá embutir todas as propriedades do produto no interior do documento pedido. Como um paralelo, o NHibernate permite configurar a propriedade cascade para “save-update” e fazer com que a classe Produto seja salva em uma tabela exclusiva para o mesmo; mais tarde, se este produto for novamente comprado, então ela reutilizaria o mesmo registro. Abaixo o código que utilizamos para inserir o pedido e o JSON que foi armazenado na base:

var repositorioDePedidos = new RepositorioDePedidos(RavenDBContext.Store.OpenSession());

var produto = new Produto() { Descricao = "Mouse Microsoft", Valor = 120M };
var pedido = new Pedido();

pedido.AdicionarItem(new Pedido.Item(produto, 2));

repositorioDePedidos.Salvar(pedido);
{
    "Data": "2017-01-17T16:59:58.2372992",
    "Total": 240,
    "Itens": [
        {
            "Produto": {
                "Descricao": "Mouse Microsoft",
                "Valor": 120,
                "Id": null
            },
            "Quantidade": 2,
            "Total": 240
        }
    ]
}

O grande ponto negativo deste modelo, é que se precisarmos alterar qualquer informação no produto, teremos que varrer todos os pedidos existentes e fazer a tal alteração.

PorId

A opção por id faz com que a referência para o produto seja armazenada, ou seja, o item passa agora a referenciar o id do produto que foi comprado que, por sua vez, e acaba sendo armazenado em uma coleção a parte dos pedidos. Com isso, os dados do produto não serão duplicados, mas você terá que saltar para outro documento para ter acesso às informações sobre o produto que foi comprado. A classe Item sofre uma pequena alteração para armazenar apenas o Id do produto:

public class Item
{
    public Item(Produto produto, int quantidade)
    {
        this.ProdutoId = produto.Id;
        this.Quantidade = quantidade;
        this.Total = produto.Valor * quantidade;
    }

    public string ProdutoId { get; private set; }

    public int Quantidade { get; private set; }

    public decimal Total { get; private set; }
}

Agora o produto deve ser armazenado separadamente em nossa base, afinal, é muito provável que ele seja um agregado na perspectiva do domínio, o que o confere direito de ter um repositório. Obviamente que antes de comprarmos algo, ele deve estar previamente cadastrado, e é esta instância deste produto já cadastrado que é passada para o item do pedido, que o reutilizará:

using (var session = RavenDBContext.Store.OpenSession())
{
    var repositorioDePedidos = new RepositorioDePedidos(session);
    var repositorioDeProdutos = new RepositorioDeProdutos(session);

    var produto = new Produto() { Descricao = "Mouse Microsoft", Valor = 120M };

    repositorioDeProdutos.Salvar(produto);

    var pedido = new Pedido();

    pedido.AdicionarItem(new Pedido.Item(produto, 2));

    repositorioDePedidos.Salvar(pedido);
}
{
    "Data": "2017-01-17T17:11:49.7687856",
    "Total": 240,
    "Itens": [
        {
            "ProdutoId": "produtos/1",
            "Quantidade": 2,
            "Total": 240
        }
    ]
}

Redundante

Por fim, esta opção nos permite criar uma classe e copiar somente os campos necessários do objeto de origem. A ideia é trazer para o item tudo o que é necessário para realizar a compra, e mesmo que no futuro as informações mudem, o pedido atual terá armazenado as informações originais do momento da compra. Quando, por exemplo, a descrição do produto for alterada, não mais refletirá nos pedidos já realizados.

Para isso, a classe Item passa a armazenar também a descrição do produto, além do valor que já vinha fazendo. Não há mais qualquer referência com a classe Produto.

public class Item
{
    public Item(Produto produto, int quantidade)
    {
        this.Descricao = produto.Descricao;
        this.Quantidade = quantidade;
        this.Total = produto.Valor * quantidade;
    }

    public string Descricao { get; private set; }

    public int Quantidade { get; private set; }

    public decimal Total { get; private set; }
}

O código que armazena o pedido nada muda em relação ao que vimos no primeiro cenário, porém se inspecionarmos o JSON da base de dados, veremos que ele armazena apenas as propriedades da classe Item, sem qualquer menção ao Produto, muito menos ao seu Id.

using (var session = RavenDBContext.Store.OpenSession())
{
    var repositorioDePedidos = new RepositorioDePedidos(session);

    var produto = new Produto() { Descricao = "Mouse Microsoft", Valor = 120M };
    var pedido = new Pedido();

    pedido.AdicionarItem(new Pedido.Item(produto, 2));

    repositorioDePedidos.Salvar(pedido);
}
{
    "Data": "2017-01-17T17:21:39.6605485",
    "Total": 240,
    "Itens": [
        {
            "Descricao": "Mouse Microsoft",
            "Quantidade": 2,
            "Total": 240
        }
    ]
}

A utilização depende de cada situação que temos. Em muito casos quando trabalhamos com base de dados relacional, não nos atentamos para algumas situações, que podem gerar problemas mais sérios em termos de negócios, como por exemplo, não armazenar o nome ou o endereço do cliente no momento da realização da compra. No caso do RavenDB a redundância é natural, mas, como disse, nem sempre ela é ruim.

O código fonte dos exemplos utilizados no decorrer este artigo está disponível neste endereço.

O custo do JOIN

Considere o exemplo exibido na imagem a seguir. Trata-se das notas fiscais emitidas para um determinado cliente, e por questões de normalização, o cliente (e todos os seus dados) é armazenado em uma tabela separada da nota fiscal, para assim evitar duplicidade de informações. A transportadora será discutida mais adiante.

join01

Podemos identificar na imagem acima que nossa aplicação é responsável por emitir notas fiscais contra um determinado cliente e apresenta-las na tela. Vamos pensar que esta aplicação nada mais é que um simples CRUD. Quando uma nota fiscal é emitida para o cliente “Israel Aece Ltda”, uma linha é adicionada na tabela NotaFiscal. Para exibirmos as notas fiscais emitidas na aplicação, basta utilizarmos a seguinte consulta:

SELECT
      nf.Data
    , nf.Total
    , c.Nome As Cliente
    , t.Nome As Transportadora
FROM NotaFiscal nf
INNER JOIN Cliente c ON c.ClienteId = nf.ClienteId
INNER JOIN Transportadora t ON t.TransportadoraId = nf.TransportadoraId

Os dados são retornados com sucesso e uma listagem é apresentada para o usuário. Daqui seis meses o nome do cliente muda para “Israel Aece Ltda em Recuperação Judicial” e precisamos novamente ter acesso às notas fiscais emitidas para ele. Ao retornar os dados, o nome que será exibido na nota fiscal já não coincide mais com a razão social da empresa da época da emissão; isso pode piorar ainda mais se outras informações mudarem, por exemplo, o endereço, algo que é comum.

Para resolver isso, podemos rastrear as alterações na tabela de clientes, criando um log de alterações para armazenar cada mudança que ocorreu no registro. Além de ser uma tarefa complicada, o JOIN ficará muito mais verboso, já que terá que contemplar outras tabelas, podendo a performance ser diretamente impactada. Armazenar na tabela NotaFiscal a razão social do cliente no momento da emissão também é uma opção, mas podemos interpretar isso de outra forma, ou seja, no mundo real, o que a nota fiscal possui é um cliente? Ou seria um destinatário?

O que vimos acima é uma aplicação no estilo CRUD, onde nossas tarefas são encaradas como simples ações (DML) a serem executadas na base de dados. A estrutura de dados é o nosso principal guia, fazendo com que a nossa aplicação tenha uma grande afinidade com ele, e tarefas triviais são difíceis de serem implementadas, como foi o exemplo que vimos acima. Pra agravar, percebemos que um mesmo conjunto de dados é compartilhado entre a escrita (emissão da nota) e leitura (exibição em tela).

Muitas vezes a emissão de uma nota fiscal é muito mais do que um simples INSERT. Se mal analisado, a aplicação que antes era só lógica de acesso à dados, começa a ser poluída com regras de negócios e o código que atendia inicialmente, começa a ficar frágil e de difícil manutenção, pois inevitavelmente vamos acabar misturando as responsabilidades, já que não haverá divisão lógica/física da arquitetura da aplicação.

Se começarmos a pensar separadamente no que precisamos fazer para atender a regra de negócio (emissão da nota fiscal) do que precisamos fazer para exibir na tela (listagem de notas emitidas), o resultado vai sair muito melhor, ou, no mínimo, vai provocar discussões que certamente ajudarão na modelagem da arquitetura. Considere o código a seguir:

public class Cliente
{
    public string Nome { get; set; }
}

public class NotaFiscal
{
    public NotaFiscal(Cliente cliente)
    {
        this.Destinatario = new DadosDoDestinatario()
        {
            Nome = cliente.Nome
        };
    }

    public DadosDoDestinatario Destinatario { get; private set; }

    public class DadosDoDestinatario
    {
        public string Nome { get; set; }
    }
}

Deixando o modelo CRUD em detrimento ao modelo orientado ao domínio (DDD), os elementos ficam muito mais evidentes, como podemos perceber acima. Note que a nota fiscal não possui referência direta com um cliente, mas sim um destinatário da mercadoria, e para facilitar, criamos um construtor que aceita o cliente como parâmetro e copia os dados necessários (o nome, para este exemplo) para emissão da nota fiscal. A transportadora, que até então não falamos dela, está associada à nota fiscal e é um item importante ao exibir na tela quando formos apresentar as notas fiscais.

Ao persistir as classes acima em uma base de dados relacional, o custo do JOIN para extrair o respectivo cliente não existirá mais, pois o destinatário, que passa a ser uma coluna na tabela NotaFiscal, terá a informação. O JOIN ainda será necessário para chegar até a transportadora. Isso pode aumentar ainda mais, por exemplo, para exibir os itens comprados, pois precisaremos fazer JOIN com a tabela de produtos. Nada disso é tão ruim, porém se o banco de dados começar a ser o principal gargalo da aplicação, vai ser difícil conseguir ter uma estrutura performática para atender o lado da escrita e o lado da leitura.

Separar as bases de dados pode ser uma opção, permitindo com que a estrutura do lado da escrita seja uma base mais normalizada, enquanto do lado da leitura, queremos otimizar para termos uma performance extraordinária. Em geral, o custo do armazenamento é mais barato do que o custo de processamento, então a redundância de informações será benéfica e o custo será baixo. Com isso, o lado da leitura passa a ter a relação de todas as notas fiscais emitidas sem a necessidade de realizar JOINs para complementar informações. É importante dizer que nada impede de um lado utilizar uma base de dados relacional e da outra uma base de dados orientada à documentos; independente disso, a dificuldade aqui é em como fazer a sincronização destas bases de dados. Continua.

Middlewares para Comandos

Quando optamos por separar a leitura da escrita em nossa aplicação, é comum adotarmos o padrão CQRS. Entre os vários conceitos que temos, um deles é a criação de comandos, que nada mais são do que classes que expressam a intenção do usuário em alterar, criar ou excluir alguma informação. O comando é definido através de uma classe, que traz informações (dados) sobre a ação a ser executada. Ele é passado para o que chamamos de command handler, que é o tratador deste comando, ou seja, ele coordena todas as ações sobre aquela entidade. E, da mesma forma, também é implementada através de uma classe (apesar de haver algumas variações). Em geral, a relação um tratador para cada comando.

O código abaixo exibe um exemplo simples disso. A classe Command não traz qualquer informação adicional, é apenas uma convenção; já a interface IHandler<T> define a estrutura que todo tratador de comando deverá ter, ou seja, o método Handle. É no interior deste método que recorremos ao repositório para adicionar/remover a entidade, manipular, etc.

public class CadastrarUsuario : Command
{
    public string Nome { get; set; }
}

public class CadastrarUsuarioHandler : IHandler<CadastrarUsuario>
{
    public CadastrarUsuarioHandler(IRepositorioDeUsuarios repositorio) { }

    public void Handle(CadastrarUsuario message)
    {
        Console.WriteLine("Cadastrando o usuário " + message.Nome);
    }
}

Para manter a simplicidade, este código está apenas escrevendo o nome do usuário na tela. Em um ambiente real, é provável termos que incrementar isso. Para citar alguns exemplos, é comum ter a necessidade de realizar logs, mensurar o tempo gasto na operação, proteger as ações através de uma transação, etc. A partir do momento que começarmos a incorporar essas atividades em nosso tratador, ele começará a ficar poluído, e o pior, começa a ter mais de uma responsabilidade além daquela para qual ele foi criado. Note o mesmo tratador que temos acima, contendo estes incrementos:

public class CadastrarUsuarioHandler : IHandler<CadastrarUsuario>
{
    public CadastrarUsuarioHandler(
        ILogger logger, 
        ITimer timer, 
        IUnitOfWork transaction, 
        IRepositorioDeUsuarios repositorio) { }

    public void Handle(CadastrarUsuario message)
    {
        this.timer.Start();
        this.logger.Info("Início");

        using (this.transaction)
        {
            this.repositorio.Salvar(message.Nome);
            Console.WriteLine("Cadastrando o usuário " + message.Nome);
        }

        this.logger.Info("Fim");
        this.timer.Stop();
    }
}

Mesmo que estamos recebendo as implementações do mundo exterior, a classe CadastrarUsuarioHandler está manipulando mais coisas do que deveria. Quando criarmos um segundo comando, teremos que repetir todos estes códigos, ficando sujeito a erros e com pouca reusabilidade.

O melhor a fazermos aqui é remover todo este código de cross-cutting, deixando o tratador do comando responsável apenas por criar e armazenar o usuário na base de dados através do repositório. Todas essas outras – importantes – atividades podem ser delegadas para classes que circundam todos os tratadores da aplicação, não ficando restrito a apenas um, e a partir daí, tendo um ponto central para isso, fica fácil a manutenção e eventual ajuste na interface dos componentes, já que não precisamos sair refatorando em diversos lugares da aplicação.

Uma das técnicas para isso, é criar um pipeline de funcionalidades, onde o item mais baixo de ações é a execução do tratador do comando. Até ele chegar no tratador propriamente dito, ele passaria pelas funcionalidades de log, timer e transação, deixando o ambiente todo preparado para a execução da atividade principal. A ideia é criar middlewares, próximo ao que temos no ASP.NET Core, para que possamos acoplar ou desacoplar alguma funcionalidade quando necessário. Além da extensibilidade, temos uma separação bem definida do que cada middleware deve fazer, sem um afetar no trabalho doutro. A imagem abaixo ilustra o exemplo:

commandhandlermiddleware1

O primeiro passo é a criação da interface que irá definir a estrutura de um middleware. Basicamente ele fornecerá um método chamado Execute, que além da instância do comando a ser executado, ela também recebe o método a ser executado na sequência. Opcionalmente, você pode optar por receber o delegate da próxima ação no construtor do middleware.

public interface IMiddleware
{
    void Execute(Command command, Action<Command> next);
}

A partir daí podemos ir criando os middlewares necessários para a nossa aplicação. Abaixo temos o exemplo do TimerMiddleware, que utiliza um Stopwatch para mensurar o tempo gasto na execução do comando. É importante notar que podemos ter códigos sendo executado antes e depois da próxima ação.

public class TimerMiddleware : IMiddleware
{
    public void Execute(Command command, Action<Command> next)
    {
        var timer = Stopwatch.StartNew();
        Console.WriteLine("[INFO] - Início - {0:HH:mm:ss}", DateTime.Now);

        try
        {
            next(command);
        }
        finally
        {
            timer.Stop();
        }

        Console.WriteLine("[INFO] - Fim - {0:HH:mm:ss} - Tempo Decorrido: {1}", 
            DateTime.Now, timer.Elapsed);
    }
}

Só que este middleware por si só não funciona. Temos agora que criar uma estrutura que consiga receber os middlewares e, consequentemente, montar uma cadeia de chamadas na ordem desejada para execução. A ideia é receber todos os middlewares necessários através do construtor do pipeline, e com isso, já criar uma sequência de execução, para que ela já esteja pronta quando chegar para o pipeline um novo comando. É importante notar aqui que neste cenário, o command bus (mencionado em arquitetura CQRS) é implementado como um middleware.

public class Pipeline
{
    private Action<Command> middlewareChain;

    public Pipeline(params IMiddleware[] middlewares)
    {
        this.middlewareChain = BuildChain(middlewares.Reverse().ToArray());
    }

    private static Action<Command> BuildChain(IMiddleware[] middlewares)
    {
        Action<Command> chain = command => { };

        foreach (var middleware in middlewares)
        {
            var temp = chain;

            chain = command => middleware.Execute(command, temp);
        }

        return chain;
    }

    public virtual void Handle<T>(T command) where T : Command
    {
        this.middlewareChain(command);
    }
}

Se quiser postergar a criação da cadeia de chamada para a primeira execução, podemos declarar o campo middlewareChain como Lazy<T>. E, por fim, basta instanciarmos o pipeline de acordo com a nossa estrutura de middlewares necessária para a aplicação, e a partir daí, passar os comandos que estão sendo requisitados pelos usuários. Novamente, note que o middleware CommandHandlerMiddleware é o último da lista:

var pipeline = 
    new Pipeline(
        new TimerMiddleware(), 
        new LoggingMiddleware(), 
        new TransactionMiddleware(), 
        new CommandHandlerMiddleware());

pipeline.Handle(new CadastrarUsuario() { Nome = "Israel" });

O código na íntegra está disponível neste link.

Rescrevendo Controllers

Codificar as ações (métodos) dentro de um controller não é uma tarefa difícil. A implementação varia muito, e todas elas podem ser consideradas corretas se atingirem o objetivo esperado. A finalidade deste vídeo é apresentar uma forma alternativa para a escrita de controllers e ações, mantendo o mesmo comportamento, porém organizando, estruturando e reaproveitando melhor os códigos que serão executados.

A ideia é mostrar uma outra forma para escrever controllers do ASP.NET MVC ou Web API, mas o conceito pode ser estendido para outros tipos de aplicação que estão além do mundo web.

Eventos de Domínio – Outra Opção de Disparo

Nos artigos anteriores falamos sobre a geração e consumo de eventos de domínio. Entre os assuntos abordados, discutimos os tratadores, que nada mais são que classes que são executadas reagindo ao evento que foi disparado. Ainda falando sobre os tratadores, abordamos a forma de descobrir os tratadores que fazem parte da aplicação (estática ou dinâmica) bem como a possibilidade de incluir novos tratadores em tempo de execução.

Para recapitular, temos a classe DomainEvents, qual utilizamos para disparar os eventos. Nos exemplos anteriores, esta classe estava sendo utilizada no interior das entidades, que quando era detectado a necessidade de disparo de algum evento, recorria ao método Raise, informando o tipo do evento e suas respectivas informações.

public void Lancar(Lancamento lancamento)
{
    var saldoAnterior = this.Saldo;

    this.lancamentos.Add(lancamento);
    this.Saldo += lancamento.Valor;

    DomainEvents.Raise(
        new SaldoDaContaAlterado(this.NomeDoCliente, saldoAnterior, this.Saldo));
}

O problema desta técnica é que a entidade além de criar o evento, também está sendo responsável por disparar ele, e se algum problema acontecer depois do disparo de evento que notifica a alteração do saldo, não é fácil desfazer o que já foi realizado pelo(s) tratador(es). Existem situações em que não dá para assegurar que depois do evento disparado as informações serão corretamente persistidas sem que algum erro ocorra. Considere o exemplo de código a seguir:

var repositorio = new RepositorioDeContas();

var cc = new ContaCorrente("Israel Aece");
cc.Lancar(new ContaCorrente.Lancamento("Pagto de Energia", -1000));

repositorio.Atualizar(cc);

Conforme vimos anteriormente, o método Lancar dispara o evento e o tratador adiciona o cliente para monitoramento. Imagine agora que ao invocar o método Atualizar do repositório, algum exceção ocorra. A complexidade para ir até o monitor e desfazer a inserção do cliente seria muito custosa e de difícil implementação. Isso poderia piorar ainda mais se estivermos trabalhando entre contextos distintos, que podem estar fisicamente separados.

Felizmente temos uma alternativa para melhorar a implementação e o disparo dos eventos, combinando isso com o repositório da entidade. Ao invés das entidades gerarem e dispararem os eventos, criamos internamente uma coleção destes eventos para que ela vá armazenando todos os acontecimentos, e ao atualizar na base de dados, percorremos todos os eventos, disparando cada um deles. Para uma melhor reutilização de código, criamos uma classe base para todas as entidades, ou melhor, para os aggregate roots.

public abstract class Entidade
{
    private readonly IList<IDomainEvent> eventos = 
        new List<IDomainEvent>();

    protected void AdicionarEvento(IDomainEvent evento)
    {
        this.eventos.Add(evento);
    }

    public void RemoverEventos()
    {
        this.eventos.Clear();
    }

    public IEnumerable<IDomainEvent> Eventos
    {
        get
        {
            return this.eventos;
        }
    }
}

Internamente esta classe armazenará a coleção de eventos, representado por instâncias de classes que implementam a interface IDomainEvent. A implementação do método Lancar tem uma suave mudança, e passa a recorrer ao método AdicionarEvento (que é protected) para adicionar o evento que indica a alteração do saldo.

public void Lancar(Lancamento lancamento)
{
    var saldoAnterior = this.Saldo;

    this.lancamentos.Add(lancamento);
    this.Saldo += lancamento.Valor;

    this.AdicionarEvento(
        new SaldoDaContaAlterado(this.NomeDoCliente, saldoAnterior, this.Saldo));
}

Isso por si só não funciona. Conforme falamos acima, temos que mudar o repositório para que ele identifique a existência de eventos e dos dispare. Mas aqui vale observar que ele somente deverá fazer isso depois que a atualização na base de dados (INSERT, UPDATE ou DELETE) seja realizada com sucesso. Os tratadores agora podem realizar suas atividades sem a preocupação de que aquilo poderia, em algum momento, ser desfeito.

Para manter a simplicidade, implementei o disparo dos eventos diretamente no repositório de contas, mas é possível refatorar o repositório a fim de criar uma base para todos os repositórios da aplicação, reutilizando o disparo de eventos para todas as entidades, já que o processo será o exatamente o mesmo. O método DispararEventos deve ser chamado sempre que a adição, atualização ou exclusão for realizada, e assim iteramos pela coleção de eventos (exposta pela classe abstrata Entidade) e invocamos o método estático Dispatch da classe DomainEvents. Por fim, depois dos eventos disparados, removemos os mesmos da entidade, já que se alguma coisa nova acontecer a partir dali, estes já estão concluídos.

public class RepositorioDeContas : IRepositorio<ContaCorrente>
{
    public void Atualizar(ContaCorrente entidade)
    {
        //Atualizar Base de Dados

        DispararEventos(entidade);
    }

    private static void DispararEventos(Entidade entidade)
    {
        foreach (var evento in entidade.Eventos)
            DomainEvents.Dispatch(evento);

        entidade.RemoverEventos();
    }
}

O método Dispatch tem funcionalidade semelhante ao Raise, mas soa melhor neste cenário, já que aqui ele tem a função de delegar o disparo dos eventos criados pelas entidades para os tratadores. Ao contrário do método Raise, que é genérico, o método Dispatch lida diretamente com instâncias da interface IDomainEvent ao invés dos eventos concretos. Por fim, ele analise se o tipo do evento que o tratador implementa é igual ao evento disparado, e o executa.

public static void Dispatch(IDomainEvent @event)
{
    foreach (var handler in handlers)
        if (handler.GetInterfaces()
                   .Any(h => h.IsGenericType && h.GenericTypeArguments[0] == @event.GetType()))
            ((dynamic)Activator.CreateInstance(handler)).Handle((dynamic)@event);
}

Eventos de Domínio – Disparo e Consumo

No artigo anterior falamos sobre a criação e utilização de eventos de domínio. O artigo abordou até o momento do disparo do evento propriamente dito, através da classe DomainEvents, só que sem mostrar detalhes de sua implementação. Existem diversas formas de se implementar o método de disparo do evento, mas antes de falarmos sobre estas técnicas, precisamos abordar como se constrói os consumidores dos eventos.

Para especificarmos os tratadores, vamos criar uma interface que descreverá apenas um método: Handler. Só que trata-se de uma interface genérica, onde o tipo T deve ser alguma classe que obrigatoriamente implemente a interface IDomainEvent, e que para o exemplo que estamos utilizando e evoluindo é a classe SaldoDaContaAlterado.

public interface IHandler<T> 
    where T : IDomainEvent
{
    void Handle(T @event);
}

Com a interface criada, temos que implementar a mesma em classes que serão consideradas os tratadores dos eventos, substituindo o tipo T por algum evento que nosso domínio define e estamos interessados em sermos notificados quando ele acontecer. Dentro da implementação do método Handle ficamos livres para executar tudo o que for necessário para aquele contexto, e que no nosso caso, é colocar “uma lupa” sobre o cliente que está com saldo negativo. Note que como parâmetro do método Handle temos (ou deveríamos ter) todas as informações necessárias a respeito do que ocorreu.

public class MonitorDeClientes : IHandler<SaldoDaContaAlterado>
{
    public void Handle(SaldoDaContaAlterado @event)
    {
        if (@event.SaldoAtual < @event.SaldoAnterior)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(
                "Monitorando o Cliente {0}. Saldo: {1:N2}",
                @event.NomeDoCliente,
                @event.SaldoAnterior);

            Console.ResetColor();
        }
    }
}

Uma vez que a classe concreta está criada e implementada, precisamos acopla-la a execução para que ela seja executada. Agora fazemos o uso da classe DomainEvents para acomodar a relação dos eventos de domínio. Aqui temos duas formas de proceder, sendo uma lista de tratadores estáticos ou de tratadores dinâmicos. Os tratadores estáticos permitem à aplicação já identificar todos os tratadores existentes, em outras palavras, podemos utilizar Reflection para encontrar todas as classes que implementam a interface IHandler<T> e adiciona-las a coleção de tratadores da aplicação, e também via Reflection, instanciarmos essas classes que representam os eventos toda vez em que ele for disparado pela domínio.

public static class DomainEvents
{
    private static List<Type> handlers = new List<Type>();

    static DomainEvents()
    {
        handlers =
            (
                from t in Assembly.GetExecutingAssembly().GetTypes()
                from i in t.GetInterfaces()
                where
                    i.IsGenericType &&
                    i.GetGenericTypeDefinition() == typeof(IHandler<>)
                select t
            ).ToList();
    }

    public static void Raise<T>(T @event) where T : IDomainEvent
    {
        handlers.ForEach(h =>
        {
            if (typeof(IHandler<T>).IsAssignableFrom(h))
                ((IHandler<T>)Activator.CreateInstance(h)).Handle(@event);
        });
    }
}

O uso externo da classe ContaCorrente não muda em nada, ou seja, continuamos interagindo com os métodos públicos que ela expõe. Como a varredura em busca por classes que implementam a interface IHandler<T> está no construtor estático da classe DomainEvents, tão logo quando a aplicação for inicializada os tipos serão identificados e adicionado, e quando o método Raise for invocado quando um lançamento de débito ou crédito ocorrer, o nome do cliente e seu saldo serão apresentados na tela em cor vermelha.

var cc = new ContaCorrente("Israel Aece");
cc.Lancar(new ContaCorrente.Lancamento("Pagto de Energia", -1000M));

A outra opção que temos é a relação dinâmica de tratadores, onde também podemos utilizar Reflection para descobrir os tratadores que implementam a interface IHandler<T>, porém há a possibilidade de dinamicamente adicionar novos tratadores em tempo de execução de acordo com a necessidade através do método Register. O método Raise agora já não instancia dinamicamente o tratador, ou seja, isso é responsabilidade do código que o consome, dando a possibilidade de fazer uso da instância antes e depois se desejar, o que pode ser útil durante os testes para saber se o evento foi o não disparado.

public class MonitorDeClientes : IHandler<SaldoDaContaAlterado>
{
    public readonly List<string> ClientesMonitorados = new List<string>();

    public void Handle(SaldoDaContaAlterado @event)
    {
        if (@event.SaldoAtual < @event.SaldoAnterior)
            this.ClientesMonitorados.Add(@event.NomeDoCliente);
    }
}

Neste modelo, para exemplificar, ao invés de escrever na tela o cliente monitorado, o adicionamos na coleção de clientes, que nada mais é que um campo da classe. E a classe DomainEvents também mudará a sua implementação para possibilitar o vínculo dinâmico de eventos, onde temos um dicionário que para cada tipo de evento uma coleção de delegates é criada.

public static class DomainEvents
{
    private static Dictionary<Type, List<Delegate>> handlers =
        new Dictionary<Type, List<Delegate>>();

    static DomainEvents()
    {
        handlers =
            (
                from t in Assembly.GetExecutingAssembly().GetTypes()
                where
                    !t.IsInterface &&
                    typeof(IDomainEvent).IsAssignableFrom(t)
                select t
            ).ToDictionary(t => t, t => new List<Delegate>());
    }

    public static void Register<T>(Action<T> handler) where T : IDomainEvent
    {
        handlers[typeof(T)].Add(handler);
    }

    public static void Raise<T>(T @event) where T : IDomainEvent
    {
        handlers[typeof(T)].ForEach(h => ((Action<T>)h)(@event));
    }
}

Por fim, o código que consome também sofrerá uma alteração para exibir o uso monitor antes e depois do evento que foi disparado.

var monitor = new MonitorDeClientes();
DomainEvents.Register<SaldoDaContaAlterado>(monitor.Handle);

var cc = new ContaCorrente("Israel Aece");
cc.Lancar(new ContaCorrente.Lancamento("Pagto de Energia", -1000));

Console.WriteLine("Qtde: {0}", monitor.ClientesMonitorados.Count);

Em ambas as técnicas é possível ter diversos tratadores para um mesmo evento gerado. Isso é comum e muito mais elegante do que em um simples tratador realizar mais tarefas do que ele deveria fazer. Se ele é responsável por monitorar, não deveria ser responsável por notificar o gerente que a conta de seu cliente ficou negativa. Nos tratadores também vale o princípio de responsabilidade única para garantir uma fácil manutenção e legibilidade.

Para finalizar, essas técnicas funcionam bem, mas existem alguns problemas funcionais que podem tornar o sistema propício a falhas. Mas isso será assunto do próximo artigo da série.

Eventos de Domínio – Geração

Os eventos de domínio nos permite identificar ações importantes que ocorrem em nossa aplicação e que desejamos divulga-la para os interessados. Por interessados, leia-se outras aplicações ou, principalmente, outros contextos que estão interligados e que reagem aos eventos para realizar uma outra atividade relacionada aquela que acabou de acontecer. Para um exemplo simples, considere uma conta corrente que ao atingir o valor negativo, a central de risco do banco deve ser acionada para entender o que está havendo com o cliente e, eventualmente, monitorar as suas atividades financeiras para evitar um prejuízo maior.

O lançamento de débito ou crédito se dá na conta corrente, e se a regra de monitoramento for atendida, temos que passar a monitorar o respectivo cliente. Incorporar eventos à classe correspondente, que neste caso é a classe que representa a conta corrente, ajudará em uma centralização de código, fácil manutenção e, principalmente, agregando à ela a responsabilidade de notificar que o saldo foi alterado (para cima ou para baixo). Competirá aos consumidores a usar a informação de acordo com a sua necessidade. O monitor de risco talvez não esteja interessado em uma conta que ficou “menos negativa”.

public class ContaCorrente
{
    //Outros membros ocultados

    public void Lancar(Lancamento lancamento)
    {
        var saldoAnterior = this.Saldo;

        this.lancamentos.Add(lancamento);
        this.Saldo += lancamento.Valor;
    }
}

Por agora, tudo o que o método acima faz é alteração da propriedade que armazena o saldo e inclui um novo lançamento na coleção interna. Depois do saldo alterado, chega o momento da conta corrente gerar o evento para notificar a alteração no saldo. A implementação padrão de eventos de domínio consiste na criação de uma marker interface, que geralmente não possui nenhum membro. Ao contrário do que acontece no .NET, onde os eventos são representados por delegates, no domínio utilizamos simples classes que implementam esta interface:

public interface IDomainEvent { }

A nomenclatura destas classes são sempre definidas no passado, que indicará que algo já ocorreu, por exemplo: NovoPedidoAdicionado, NotaFiscalEmitida, e para o nosso exemplo, SaldoDaContaAlterado. Vale lembrar que a nomenclatura deve expressar, e muito, exatamente o que ocorreu. E como já era de se esperar, todas as classes que representam eventos devem implementar a interface IDomainEvent que será útil para garantirmos a construção e uso dos tipos que envolvem a infraestrutura de eventos:

public class SaldoDaContaAlterado : IDomainEvent
{
    public SaldoDaContaAlterado(
        string nomeDoCliente, decimal saldoAnterior, decimal saldoAtual)
    {
        this.NomeDoCliente = nomeDoCliente;
        this.SaldoAnterior = saldoAnterior;
        this.SaldoAtual = saldoAtual;
    }

    public string NomeDoCliente { get; private set; }

    public decimal SaldoAnterior { get; private set; }

    public decimal SaldoAtual { get; private set; }
}

É importante notarmos que a classe que representa o evento possui algumas propriedades para descrever o que ocorreu, mas sempre temos que nos atentar em o que colocar ali, tentando manter a regra do mínimo possível necessário. Seguem algumas considerações importantes que devemos ter em mente durante a construção destas classes:

  • Entidades: é tentador colocar nestas propriedades a própria entidade que sofreu a alteração (ContaCorrente). Devemos ao máximo evitar isso, pois causará uma dependência destas classes para os interessados ao evento. Muitas vezes os eventos serão utilizados para comunicação entre contextos, e isso evitará a necessidade de referenciar a entidade (física e virtualmente). Tente optar sempre por tipos primitivos.
  • Imutabilidade: essas classes não devem ter qualquer funcionalidade (métodos), apenas os dados que correspondem ao evento gerado. As propriedades são de somente leitura e são abastecidas no construtor.
  • Ids: o Id da entidade pode ser definido no evento, mas isso só faz sentido se o consumidor já estiver colaborando com o gerador e tiver condições de recarregar a entidade, ou seja, ter acesso ao mesmo repositório. Caso o acesso não seja possível, teremos que incorporar na classe todas as informações necessárias para reportar a alteração, nem que seja necessário a duplicação de todas as informações da entidade.

Uma vez que os eventos já estão definidos, chega o momento de disparar. Como mencionado acima, o responsável pelo disparo será a própria classe que identifica a mudança, e que neste caso é a ContaCorrente, e sem qualquer análise de outra condição, dispara o evento informando que o saldo foi alterado. Note que ela irá recorrer à classe estática chamada de DomainEvents.

public class ContaCorrente
{
    //Outros membros ocultados

    public void Lancar(Lancamento lancamento)
    {
        var saldoAnterior = this.Saldo;

        this.lancamentos.Add(lancamento);
        this.Saldo += lancamento.Valor;

        DomainEvents.Raise(
            new SaldoDaContaAlterado(this.NomeDoCliente, saldoAnterior, this.Saldo));
    }
}

Agora é de responsabilidade da classe DomainEvents encontrar os consumidores deste tipo de evento e notifica-los da alteração. Há diversas técnicas que podemos utilizar na implementação da classe DomainEvents bem como em seus consumidores, mas que merece um artigo específico, e será abordado na sequência desta série.

Explorando o EventStore – Overview

Recentemente em dois vídeos onde abordei a técnica de event sourcing, fiz uso de uma espécie de base de dados que armazena eventos que ocorrem na aplicação, e que é chamada de Event Store. Naqueles exemplos, a ideia era armazenar os eventos gerados pelo domínio e que eram, mais tarde, utilizando para reconstruir o estado atual do objeto em questão.

Como lá abordamos o Event Store de forma bem superficial, pois o foco era no conceito e não na ferramenta, neste artigo vamos aprofundar um pouco mais para explorar a API e os recursos que este repositório pode oferecer para armazenarmos e extrairmos os eventos para uso em cenários de event sourcing ou também em cenários que demandam um service bus, onde podemos publicar conteúdo e os assinantes possam ser notificados e/ou interrogar a base para saber se algo ocorreu e tomar alguma decisão sobre isso.

O primeiro passo é fazer o download através do site oficial. Este pacote trará o executável que é a “parte servidor”, ou seja, a própria base onde os eventos serão armazenados. Além disso, há também uma console administrativa web, que é acessada através de um navegador e permitirá interagir com a base e realizar diversas configurações de funcionamento e monitoramento.

Para exemplificar o uso de alguns recursos o Event Store, vamos criar três aplicações: Loja, Nota Fiscal e Transportadora. Na primeira, o cliente irá postar o pedido de compra. A segunda, Nota Fiscal, irá interrogar periodicamente o servidor procurando por novos pedidos adicionados e, consequentemente, emitir a nota fiscal. Por fim, a Transportadora irá monitorar qualquer nova nota fiscal emitida, e irá imediatamente iniciar o processo de entrega do produto.

O primeiro passo é iniciar o servidor que passará a receber as solicitações. Basicamente depois de extraído o pacote do download, basta executar o comando abaixo no prompt de comando e ele passará a receber as requisições. E depois do servidor inicializado, você pode ir até a console web administrativa através do seguinte endereço: http://localhost:2113, login “admin” e a senha “changeit”.

C:EventStore.ClusterNode.exe

Depois do servidor rodando, precisamos começar a codificar as aplicações, e para isso, o projeto também fornece diversas APIs para diferentes tecnologias, incluindo para .NET que é chamada de EventStore.Client. Utilize o comando abaixo para baixar e instalar ele através do Nuget:

PM> Install-Package EventStore.Client

Depois das três aplicações estarem com o pacote do cliente devidamente instalado, chega o momento de estabelecermos a conexão com o servidor para postar o evento de novo pedido criado na loja. A classe que serve para estabelecer a ligação com o servidor é a EventStoreConnection, e pode (e deve) ser mantida uma por aplicação para melhor reutilização dos recursos que a mesma utiliza para executar suas atividades. É importante notar que os métodos expostos são assíncronos, mas como estou utilizando aplicações console para o exemplo, forçarei a execução síncrona de todos eles.

A conexão se dá através do protocolo TCP, onde você pode utilizar o método estático Create para informar o endereço a partir de uma URI, e a porta padrão para o servidor é a 1113. Como dito acima, estou abrindo a conexão explicitamente e aguardando que a mesma seja concluída para dar sequência na utilização.

using (var conn = EventStoreConnection.Create(new Uri(“tcp://localhost:1113”)))
{
    conn.ConnectAsync().Wait();
}

Voltando ao exemplo de testes, a primeira aplicação se resume a postar um novo evento informando que um novo pedido foi criado. Note através do código abaixo como isso é definido. Não vou me preocupar em colocar a definição das classes aqui porque são muito simples e não há qualquer informação relevante dentro delas; são classes POCOs com as propriedades que refletem nosso negócio.

var
novoPedido = new NovoPedidoCriado(new Pedido()
{
    NomeDoCliente = “Israel Aece”,
    ValorTotal = 1200
});

Depois do evento criado, precisamos postar o mesmo, mas antes precisamos entender um conceito do
Event Store, chamado de Stream. Como já suspeitamos, é a forma que temos de agrupar os eventos por qualquer regra ou seção da aplicação que desejamos. Para o nosso caso, vamos chamar o stream de “Ecommerce.Pedidos”, que irá concentrar todos os eventos relacionados aos pedidos realizados em nosso loja.

A classe de conexão fornece um método chamado 
AppendToStreamAsync, e além de especificarmos o nome do stream onde queremos armazenar, temos que passar o objeto (evento) que deve ser persistido. Para encapsular e descrever o evento do ponto de vista da infraestrutura, o Event Store possui uma classe chamada EventData. Essa classe além de ter algumas características relacionadas a infraestrutura, recebe também em seu construtor o Id do evento, o nome e o objeto que representa o mesmo, e que no nosso caso está armazenado na variável “novoPedido”.

A serialização pode ser binária, mas dependendo com quem vamos interagir/integrar, utilizar JSON pode ser uma opção bem mais interessante. E para isso, estou recorrendo ao
Json.NET (Newtonsoft) para serializar e deserializar os objetos.

conn.AppendToStreamAsync(
    “Ecommerce.Pedidos”,
    ExpectedVersion.Any,
    GerarEvento(novoPedido)).Wait();

private static
EventData GerarEvento(Evento evento)
{
    return new EventData(
        evento.Id,
        evento.GetType().Name,
        true,
        Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(evento)), null);
}

Depois que o evento for postado, se acessarmos a console administrativa do
Event Store, veremos o stream e o evento criado, conforme é possível visualizar na imagem abaixo:



Na sequência vamos recorrer ao projeto de Nota Fiscal, que ficará interrogando periodicamente o servidor em busca de eventos do tipo
NovoPedidoCriado. Como os eventos são acumulativos do lado do servidor, nós, o cliente, devemos controlar os eventos que já foram processados pela aplicação, e por isso, estamos armazenando em uma variável o número do último evento, para que em uma próxima execução não corra o risco de gerar notas fiscais para os pedidos já gerados. E vale ressaltar que isso é um exemplo, e que em uma cenário real você precisa armazenar este contador em uma local seguro que resista a possíveis reinicializações da aplicação.

Da mesma forma que fazemos na criação do evento, para ler também precisamos criar a conexão com o servidor, e neste caso, vamos utilizar o método 
ReadStreamEventsForwardAsync, e como o próprio nome diz, lê os eventos na ordem em que eles foram postados.

var ultimoEventoProcessado = 0;

using
(var conn = EventStoreConnection.Create(new Uri(“tcp://localhost:1113”)))
{
    conn.ConnectAsync().Wait();

while ((Console.ReadLine() != null))
    {

        var itens = conn.ReadStreamEventsForwardAsync(
“Ecommerce.Pedidos”, 
ultimoEventoProcessado + 1, 200, false).Result;

if (itens.Events.Any())
        {
            foreach (var e in itens.Events)
                Processar(conn, e);
        }
        else
        {
            Log.Yellow(“Não há eventos para processar.”);
        }
    }
}

Os eventos extraídos são representados pela classe ResolvedEvent, que entre várias informações sobre o evento, temos o evento gerado e serializado pela nossa aplicação de novo pedido criado. Como podemos notar abaixo, o método Processar identifica que se trata de um evento deste tipo, deserializa o mesmo, o processa e depois disso, dispara um evento dizendo que a respectiva nota fiscal foi emitida, através de um novo evento.

internal static void Processar(
IEventStoreConnection conexao, 
ResolvedEvent dadosDoEvento)
{
    if (dadosDoEvento.Event.EventType == “NovoPedidoCriado”)
    {
        var novoPedido = Extrair<NovoPedidoCriado>(dadosDoEvento);

Log.Green(“NOVO PEDIDO”);
        Log.Green(“Id: ” + novoPedido.Id, 4);
        Log.Green(“Data: ” + novoPedido.Data, 4);
        Log.Green(“Cliente: ” + novoPedido.NomeDoCliente, 4);
        Log.Green(“Valor: ” + novoPedido.ValorTotal.ToString(“N2”), 4);
        Log.Yellow(“Emitindo a Nota Fiscal do Pedido”, 4);
        Log.NewLine();

conexao.AppendToStreamAsync(
            “Ecommerce.Pedidos”,
            ExpectedVersion.Any,
GerarEvento(

new NotaFiscalEmitida(
novoPedido.NomeDoCliente, 
novoPedido.ValorTotal, 
“0001.0292.2999-2881-9918.11.9999/99”))).Wait();

ultimoEventoProcessado = dadosDoEvento.Event.EventNumber;
    }
}

private static TEvento Extrair<TEvento>(ResolvedEvent dadosDoEvento) 
where TEvento : Evento
{
return JsonConvert.DeserializeObject<TEvento>(

Encoding.UTF8.GetString(dadosDoEvento.Event.Data));
}

E, para finalizar o processo, temos agora a aplicação da transportadora, que ao invés de periodicamente procurar por um evento de nota fiscal emitida, assina o stream e tão logo quando ele for incluído, a transportadora será automaticamente notificada do evento gerado. E da mesma forma que antes, abrimos a conexão, e agora utilizamos o método SubscribeToStreamAsync, informando o nome do stream que desejamos monitorar e qualquer novo evento, disparamos o método Processar, conforme pode ser visualizado abaixo:

using (var conn = EventStoreConnection.Create(new Uri(“tcp://localhost:1113”)))
{
    conn.ConnectAsync().Wait();

    conn.SubscribeToStreamAsync(
“Ecommerce.Pedidos”, 
false
(a, e) => Processar(e)).Wait();

Console.ReadLine();
}

internal static void Processar(ResolvedEvent dadosDoEvento)
{
    if (dadosDoEvento.Event.EventType == “NotaFiscalEmitida”)
    {
        var novoPedido = Extrair<NotaFiscalEmitida>(dadosDoEvento);

Log.Green(“NOVO PEDIDO – NOTA FISCAL EMITIDA”);
        Log.Green(“Id: ” + novoPedido.Id, 4);
        Log.Green(“Cliente: ” + novoPedido.NomeDoCliente, 4);
        Log.Green(“Valor: ” + novoPedido.ValorTotal.ToString(“N2”), 4);
        Log.Green(“NF-e: ” + novoPedido.ChaveDaNotaFiscalEletronica, 4);
        Log.NewLine();
    }
}

E agora, se colocarmos as três aplicações lado a lado, é possível visualizar o efeito do processamento nas três, onde uma gera o pedido, a segunda emite a nota fiscal e a terceira é notificada para iniciar o processo de transporte.

É claro que isso é um exemplo simplista, mas pode ser considerado algo parecido em uma escala maior, como por exemplo, utilizar este recurso para diálogo entre contextos do domínio, sincronização de bases de (escrita e leitura) em ambiente de CQRS ou para desempenhar qualquer outra tarefa em modo assíncrono.

Lazy Loading e Contextos de Domínio

Quando optamos por desenvolver uma aplicação orientada ao domínio (DDD), uma série de termos e conceitos devem ser entendidos para que sejam bem aproveitados e consigamos assim expressar em nosso código o que for necessário para atender a demanda daquela aplicação, serviço ou funcionalidade. O principal guia que agrupa todos esses termos é o livro do Eric Evans, chamado de Domain-Driven Design: Tackling Complexity in the Heart of Software.

Para persistência geralmente utilizamos repositórios (também descrito neste livro), que recorremos para inserir novos dados ou até mesmo para extrair os registros existentes, e mais tarde, persistir as eventuais alterações realizadas durante o processamento de alguma tarefa. E na maioria das implementações de repositórios, por trás (infraestrutura), sempre há um ORM que faz toda a mágica.

Na medida em que vamos desenvolvendo nosso domínio, vamos agregando às entidades diversas características e funcionalidades, tornando a classe cada vez mais próxima ao mundo real. E, como sabemos, os ORMs fornecem a possibilidade de habilitarmos um recurso chamado de lazy loading. Apenas para recordar, ele posterga a extração dos dados até que a mesma seja demandada, o que em outras palavras significa que a consulta será encaminhada à base de dados somente quando acessarmos a propriedade onde estão o(s) dado(s) custoso(s). É comumente relacionado à coleções, mas há situações onde se refere à outras classes ou até mesmo propriedades (como um array de bytes). Abaixo alguns exemplos (em negrito) do que poderia ser considerado como lazy loading:

public class Duplicata
{
    public AnaliseConfirmativa Confirmacao { get; private set; }

    public string Numero { get; private set; }

    public Sacado Sacado { get; }

    public IEnumerable<AcaoDeCobranca> AcoesDeCobranca
    {
        get
        {
            return acoesDeCobranca;
        }
    }

}

Com pouca configuração, os ORMs permitem postergar a carga das informações somente quando elas são solicitadas. Abaixo um pseudo-código que ilustra os vários momentos que vamos recorrer ao banco de dados; repare que somente quando acessamos as propriedades negritadas é que elas são extraídas, tornando um processo transparente para quem escreve o código.

var duplicata = repositorioDeTitulos.BuscarPorId(1);
//SELECT Numero, Sacado FROM Duplicata WHERE TituloId = 1

Console.WriteLine(duplicata.Confirmacao.Data);
//SELECT Data, Status, Confirmador FROM AnaliseConfirmativa WHERE Id = 3

foreach (var acao in duplicata.AcoesDeCobranca)
    //SELECT * FROM AcoesDeCobranca WHERE TituloId = 1
{
    //…
}

Como já era de se esperar, o ORM honra a configuração de lazy loading, e recorre ao banco somente quando de fato precisamos dos dados. Mas vamos detalhar melhor o que acontece no código acima: primeiramente recorremos ao repositório para extrair a duplicata de identificador 1. A consulta que foi feita devolve apenas os dados básicos da duplicata (número e sacado), e é tudo o que queremos até então. Logo que precisamos da parte da confirmação, uma nova consulta é feita. Por fim, quando queremos iterar pelas ações de cobrança, uma terceira consulta é feita para extrair os registros filhos.

Podemos perceber que as “partes” da duplicata são carregadas sob demanda, mas como disse anteriormente, em certos contextos, poderíamos poupar trabalho e já carregar juntamente com os dados básicos, os dados complementares para executar uma determinada ação. Vamos supor que tivéssemos dois ambientes: confirmação e cobrança. No primeiro ambiente, gostaria de que em uma única consulta me retornasse os dados inerentes ao processo de confirmação da duplicata; já no segundo ambiente, gostaria que as ações de cobrança também fossem extraídas tão logo quando a classe Duplicata fosse materializada.

Mas a configuração do ORM é única, não me permitindo customizar isso caso a caso, ambiente por ambiente. Se a performance é um ponto crucial da aplicação, é capaz de termos que começar a poluir a interface do repositório com métodos que retorne – ainda – a duplicata, mas que contextualizem para qual ambiente queremos:

var duplicata = repositorioDeTitulos.BuscarDuplicataParaConfirmacao(1);
var duplicata = repositorioDeTitulos.BuscarDuplicataParaCobranca(1);

E no interior de cada um dos métodos, recorreríamos a API do ORM para fazer a carga antecipada das informações relativas aquele contexto. Apesar de funcionar, em pouco tempo, é provável que o repositório comece a ter diversos métodos que estão ali mais para tentar “burlar” o ORM/sistema, e induzi-los a extrair os dados necessários para executar a operação desejada pelo usuário dentro daquele contexto.

Note que com um pequeno exemplo é possível ver o tamanho do problema que podemos ter ao criar um grande domínio e sem nos preocuparmos com a separação em contextos. Eles precisam ser bem pensados para tentar mantermos as entidades com a estrutura necessária para atender aquele contexto específico, caso contrário, podemos degradar a performance e termos dificuldades na manutenção e evolução da aplicação.