Introdução ao MediatR

Ao trabalhar com CQRS, o primeiro passo é criar a infraestrutura de mensagens para suportar os comandos, consultas e eventos que a aplicação tenha. Somente a partir daí é que passamos a nos preocupar com o código referente as atividades quais nossa aplicação irá desempenhar. Para evitar a criação de toda estra estrutura, podemos recorrer à uma biblioteca chamada MediatR, que já disponibiliza esta estrutura para tráfego das mensagens, incluindo a possibilidade de notificações (eventos).

O vídeo abaixo explora essa biblioteca, com exemplos simples e práticos de sua utilização. O cenário é uma loja de comércio eletrônico extremamente simples, mas que faz uso de vários recursos que a biblioteca possui, passando pelas mensagens, tratadores, notificações e customização do pipeline de execução. O código usado no vídeo está disponível neste endereço.

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.

Web Commands

Quando falamos de CQRS, temos as consultas e os comandos. Esses comandos podem ser consumidos diretamente por aplicações, mas como expor publicamente para os clientes? Uma opção comum nos dias de hoje é utilizar o protocolo HTTP, facilitando assim o consumo pelas mais diversas linguagens e tecnologias. E quais as opções no .NET para fazermos esta exposição?

O ASP.NET é um candidato para isso, já que em sua versão core está modularizado e flexível, e assim podemos otimizar o pipeline de execução com somente aquilo que seja de fato necessário para executar os comandos. A finalidade do vídeo abaixo é exibir uma forma alternativa para incorporar os comandos do domínio no ASP.NET, utilizando o mínimo de recursos possível do hosting. Link para o código do exemplo.

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.

Event Sourcing – Parte 2

Dando continuidade ao post anterior, por questões de simplicidade,  armazenávamos os eventos gerados para uma entidade em memória, algo que não é útil em ambiente de produção.

Como alternativa a isso, podemos recorrer à algum repositório que persista fisicamente as informações. O uso de uma base de dados relacional pode ser útil, porém é necessário que você utilize colunas que possam armazenar o objeto serializado (varbinary, varchar(max), etc.).

Isso é necessário porque é difícil prever o schema. Podem haver muitos eventos, novos eventos podem acontecer, novas informações podem ser propagadas; será difícil evoluir o schema na mesma velocidade.

Uma alternativa aqui é utilizar uma base no-sql. Apesar de alguns nomes já virem a cabeça, existe uma chamada GetEventStore. Ela foi desenhada para dar  suporte à cenários de event sourcing, e sua API também dispõe de métodos e facilitadores que permitem gravar e ler eventos sem dificuldades.

O GetEventStore também permite o acesso a leitura e gravação através de HTTP, possibilitando assim que outras tecnologias, incluindo JavaScript, possam também usufruir dela.

Por fim, ele também permite a gestão da base de eventos através de um interface web, onde podemos interagir, diagnosticar e monitorar os recursos que são necessários para o seu funcionamento.

O vídeo abaixo altera o projeto criado anteriormente para fazer uso do GetEventStore. E tudo o que precisamos alterar é a classe RepositorioDeEventos; o resto da aplicação se comporta da mesma forma, porém com os dados sendo persistidos fisicamente. Se desejar, pode baixar o projeto clicando aqui.

Event Sourcing – Parte 1

Todos temos uma conta bancária. Além das informações cadastrais, muito provavelmente ela também possui o saldo atual, que representa o quanto temos naquele exato momento, podendo ser positivo, negativo ou simplesmente zero.

O saldo, subindo ou descendo, queremos, obrigatoriamente, saber como ele chegou naquele valor. É importante saber o saldo que tínhamos em uma determinada data (retroativa) e o que aconteceu para ele atingir o saldo atual.

Felizmente todos os bancos dão aos seus correntistas o extrato de sua respectiva conta. É com ele que vamos conseguir enxergar o aumento ou diminuição do nosso dinheiro. Posicionado em uma data, identificando o saldo daquele dia, se viermos somando os valores (lembrando que pode ser positivo ou negativo), chegaremos ao saldo atual da conta. Claro, se nada estiver errado.

Tecnicamente falando, é provável que no banco de dados tenhamos uma estrutura de duas tabelas, onde uma teria os dados cadastrais da conta e a outra os lançamentos. A tabela pai provavelmente também irá armazenar o saldo atual da conta e outras características.

A medida em que os lançamentos são inseridos na segunda tabela, a coluna saldo da primeira vai sendo sensibilizado para refletir o saldo atual da conta. O valor atual do saldo vai sendo sobrescrito com o novo valor.

Já está claro que o importante para o dia a dia é o valor que temos disponível, ou seja, o saldo atual. Os lançamentos são necessários, até então, apenas para histórico; na maioria das vezes, eles são dispensáveis para operar a conta corrente (pagar contas, realizar saques, sacar dinheiro, etc.). Agora, considere o exemplo abaixo:

Repare que o saldo atual que temos não corresponde a soma dos lançamentos da segunda tabela. É provável que nosso sistema possua um bug, que afeta diretamente a atualização do saldo atual.

Apesar de, teoricamente, os lançamentos estarem corretos, é difícil diagnosticar o problema, já que não é possível, de uma forma fácil, reproduzir os mesmos eventos, exatamente da forma em que eles aconteceram, para só assim identificar onde e quando o problema de fato ocorreu.
Eis que entra em cena o Event Sourcing. Ao invés de armazenar o estado atual da entidade em colunas e linhas, o uso desta técnica determina que se armazene uma sequência (stream) de eventos, onde cada um deles informa a ação que foi executada sobre a entidade. Quando esses eventos são recarregados, eles são reaplicados contra a entidade, reconstruindo a mesma. Depois de todos eventos processados, a entidade terá, novamente, o seu estado (versão) atual. Neste modelo não fazemos a persistência física das propriedades na base de dados.
Versionamento
Uma das preocupações neste modelo é com o versionamento da entidade. É através de versão que vamos controlar o processamento e aplicação dos eventos. A versão irá garantir que um evento não seja aplicado à entidade depois que outro evento tenha se antecipado e alterado o estado da mesma em relação ao momento em que ela foi construída, ou melhor, carregada.
Isso é conhecido como concorrência otimista. É algo que também já fazemos com o banco de dados relacional, comparando o valor anterior com o atual que está na tabela antes de proceder com o comando de UPDATE ou de DELETE. O controle pode ser feito de várias maneiras, mas para ser simplista, podemos nos ater ao uso de um número inteiro, que vai sendo incrementado a medida em que um novo evento é aplicado.
Snapshots
Ao longo do tempo, mais e mais eventos vão sendo adicionados e, eventualmente, a performance pode ser degradada. Neste caso, uma possível solução é a criação de snapshots, que depois de carregado os eventos, reaplicados na entidade, ela pode disponibilizar métodos para expor o seu estado atual.
Os snapshots também são armazenados e quando a entidade for recarregada, primeiramente devemos avaliar a existência dele antes de reaplicar os eventos. Se houver um snapshot, restauramos o estado da entidade e reaplicaremos os eventos que aconteceram do momento do snapshot para frente, não havendo a necessidade de percorrer toda a sequência de eventos, ganhando assim em performance. Tão importante quanto o estado atual, o snapshot também deve armazenar a versão atual da entidade, pois esta informação é essencial para o correto funcionamento dos snapshots. Essa rotina de criação de snapshots pode ser feita em background, em uma janela de baixa atividade do sistema, podendo ser implementado através de uma tarefa do Windows que roda periodicamente ou até mesmo com um Windows Service. Para a criação de snapshots, podemos recorrer ao padrão Memento, que nos permite expor e restaurar o estado sem violar o encapsulamento do objeto.

Naturalmente o cenário de conta corrente que vimos acima quase se encaixa perfeitamente neste modelo, exceto pelo fato de termos o saldo atual sumarizado e armazenado fisicamente. Vamos então transformar uma simples classe com uma coleção de lançamentos em um modelo que suporte a técnica descrita pelo Event Sourcing. Vale lembrar que isso é “apenas” mais uma forma de persistência. Você pode ter situações que talvez esse modelo seja útil e necessário, já para outros ambientes, pode ser considerado overkill.

Web Sockets com ASP.NET Web API

Há algum tempo eu comentei sobre a possibilidade do WCF expor serviços para serem consumidos utilizando a tecnologia de Web Sockets. Neste mesmo artigo eu falei sobre os detalhes do funcionamento deste protocolo, e que se quiser saber o seu funcionamento e implementação em um nível mais baixo, aconselho a leitura.

Como sabemos, o WCF é uma tecnologia que roda do lado do servidor. Da mesma forma que temos o ASP.NET Web API, que é uma tecnologia que utilizamos para criarmos APIs e expor para os mais diversos tipos de clientes. Assim como a Microsoft adicionou o suporte à web sockets no WCF, ela também fez o mesmo com o ASP.NET Web API e MVC, ou seja, incorporou no próprio framework o suporte para criação de recursos que são expostos utilizando web sockets.

É importante dizer que a Microsoft também criou uma outra tecnologia chamada de SignalR, que fornece diversos recursos para a criação de aplicações que precisam gerar e consumir informações que são consideradas de tempo real. Este framework já possui classes que abstraem a complexidade de exposição destes tipos de serviços, fornecendo classes de mais alto nível para trabalho.

Entre os novos tipos que foram adicionados, temos a propriedade IsWebSocketRequest (exposta pela classe HttpContext), que retorna um valor boleano indicando se a requisição está solicitando a migração do protocolo para web sockets. Caso seja verdadeiro, então recorremos ao método AcceptWebSocketRequest da mesma classe para especificarmos a função que irá periodicamente ser executada.

Para o exemplo, vamos criar um publicador de notícias, que quando o cliente optar por assiná-lo, ele irá receber as notícias de forma randômica em um banner a cada 2 segundos. Como podemos ver no código abaixo, o método Assinar da API é exposto para que os clientes cheguem até ele através do método GET do HTTP. Como se trata de uma solicitação de migração, retornamos o código 101 do HTTP indicando a troca do protocolo para web sockets.

public class PublicadorController : ApiController
{
    private static IList<string> noticiais;
    private static Random random;

    static PublicadorController()
    {
        noticiais = new List<string>()
        {
            “You can buy a fingerprint reader keyboard for your Surface Pro 3”,
            “Microsoft’s new activity tracker is the $249 Microsoft Band”,
            “Microsoft’s Lumia 950 is the new flagship Windows phone”,
            “Windows 10 will start rolling out to phones in December”,
            “Microsoft’s Panos Panay is pumped about everything (2012–present)”
        };

        random = new Random();
    }

    [HttpGet]
    public HttpResponseMessage Assinar()
    {
        var httpContext = Request.Properties[“MS_HttpContext”] as HttpContextBase;

        if (httpContext.IsWebSocketRequest)
            httpContext.AcceptWebSocketRequest(EnviarNoticias);

        return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
    }

    private async Task EnviarNoticias(AspNetWebSocketContext context)
    {
        var socket = context.WebSocket;

        while (true)
        {
            await Task.Delay(2000);

            if (socket.State == WebSocketState.Open)
            {
                var noticia = noticiais[random.Next(0, noticiais.Count – 1)];
                var buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(noticia));

                await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
            }
            else
            {
                break;
            }
        }
    }
}

Já dentro do método EnviarNoticias criamos um laço infinito que fica selecionando randomicamente uma das notícias do repositório e enviando para o(s) cliente(s) conectado(s). É claro que também podemos receber informações dos clientes conectados. Para o exemplo estou me limitando a apenas gerar o conteúdo de retorno, sem receber nada. Se quiser, pode utilizar o método ReceiveAsync da classe WebSocket para ter acesso à informação enviada por ele. E, por fim, para não ficar eternamente rodando o código, avaliamos a cada iteração se o estado da conexão ainda continua aberto, caso contrário, encerramos o mesmo.

E do lado do cliente, se for uma aplicação web, vamos recorrer ao código Javascript para consumir este serviço. Temos também a disposição deste lado a classe WebSocket, que quando instanciada, devemos informar o endereço para o método que assina e migra o protocolo para web sockets. Repare que a instância é criada dentro do evento click do botão Conectar e também já nos associamos aos eventos – autoexplicativos – onopen, onmessage e onclose (e ainda tem o onerror).

<!DOCTYPE html>
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head>
    <title></title>
    https://code.jquery.com/jquery-2.1.4.min.js
   
        var ws;

        $().ready(function ()
        {
            $(“#Conectar”).click(function () {
                ws = new WebSocket(“ws://localhost:2548/api/Publicador/Assinar”);

                ws.onopen = function () {
                    $(“#Status”).text(“Conectado. Recuperando Notícias…”);
                };

                ws.onmessage = function (evt) {
                    $(“#Status”).text(“”);
                    $(“#Banner”).text(evt.data);
                };

                ws.onclose = function () {
                    $(“#Banner”).text(“”);
                    $(“#Status”).text(“Desconectado”);
                };
            });

            $(“#Desconectar”).click(function () {
                ws.close();
            });
        });
   
</head>
<body>
    <input type=”button” value=”Conectar” id=”Conectar” />
    <input type=”button” value=”Desconectar” id=”Desconectar” />
    <br /><br />
    <span id=”Status”></span>
    <span id=”Banner”></span>
</body>
</html>

Quando acessar a página HTML e clicar no botão Conectar, o resultado é apresentado abaixo. A qualquer momento podemos pressionar no botão Desconectar e indicar ao servidor que não estamos mais interessados no retorno das informações. A cada envio de mensagem do servidor para o cliente, o evento onmessage é disparado, e estamos exibindo a mensagem em um campo na tela.

Apenas para informação, o Fiddler é capaz de identificar quando a conexão é migrada para web sockets, e a partir daí consegue capturar as mensagens que são enviados para o cliente. A imagem abaixo é possível visualizar o log capturado depois da assinatura realizada: