Setembro de 2015

Volume 30 - Número 9

Programas de ponta - Event Sourcing para aplicativos comuns

Dino Esposito | Setembro de 2015

Dino EspositoÉ uma atividade tão comum e natural que você talvez nem lhe dê muita atenção. Ao pensar em armazenamento de dados, você naturalmente considerar um formato que simplesmente preserve o estado atual dos dados. Embora haja alguns sistemas de grande porte, como no setor bancário ou de seguros, em que todas as ações de software são cuidadosamente controladas e registradas, a maioria dos aplicativos e sites da Web precisa apenas salvar o estado atual de dados.

A abordagem do estado atual faz um instantâneo do estado do sistema e o torna persistente. Normalmente, os dados residem em um banco de dados relacional. Isso é o suficiente para realizar novas transações e recuperar os resultados das transações anteriores. Cenários em que o armazenamento de estado atual é insuficiente não foram muito comuns durante a última década.

Hoje em dia, porém, o cenário de negócios está mudando rapidamente. O controle de eventos de negócios e de domínio está se tornando cada vez mais uma necessidade. O Event Sourcing (ES) é um padrão que afeta a arquitetura de armazenamento e a forma como você pode recuperar e inserir dados armazenados. O ES não se limita a auditar e registrar eventos relevantes para o negócio em um domínio persistente. Ele também usa um nível de abstração mais baixo para salvar seus dados e usar ferramentas e padrões ad hoc para criar várias projeções de dados.

O ES pode parecer uma maneira inteligente e interessante para registrar e auditar funções comerciais, mas é realmente um novo modelo de teoria de armazenamento tão relevante quanto o modelo relacional tem sido desde o início. Ele pode ter um impacto maior em softwares modernos do que o armazenamento NoSQL. O ES não é uma alternativa aos produtos relacionais e NoSQL atuais. Você pode implementar o ES no topo de bancos de dados relacionais e de armazenamentos de dados NoSQL. O ES muda a visão sobre o armazenamento de aplicativos e uso de eventos em vez de valores com informação de estado, como tokens de dados.

Quando o Event Sourcing ajuda?

Uma maneira básica, embora comum, para ir além do armazenamento de estado atual é rastrear o histórico de atualizações. Imagine um aplicativo simples para livrarias. Cada livro tem uma propriedade de descrição e concede permissões de edição aos usuários. É preciso controlar uma descrição antiga quando um usuário insere uma nova?

Cada um tem um necessidades distintas, mas considere que neste exemplo é importante controlar as atualizações. Como você implementaria esse controle? Uma opção é armazenar o estado atual do livro e registrar quaisquer detalhes de atualização em uma tabela separada. Assim, cada atualização teria um registro correspondente. O registro atualizado conteria o delta de atualização, como os valores novos e antigos de cada coluna modificada.

Existe também outra maneira de fazer. A tabela Livros pode conter vários registros para o mesmo catálogo marcados com determinada ID. Cada registro representaria um estado com carimbo de data/hora listado em ordem (ver Figura 1).

Vários registros retêm o histórico da entidade
Figura 1 Vários registros retêm o histórico da entidade

Esse cenário requer uma API ad hoc para ler o estado atual do registro. Não é simplesmente uma consulta no repositório que seleciona o registro por ID. Você deve escolher aquele com o carimbo de data/hora mais recente ou o maior número progressivo atualizado. Além disso, a união de todos os eventos relacionados a uma determinada entidade de dados formam um fluxo. Esse fluxo de eventos é um conceito conhecido no ES.

O ES ajuda nas demandas de negócios sempre que você rastrear uma série de eventos. Embora se pareça com interesses transversais como registros em log ou auditorias, o ES é bastante diferente. Ele não registra eventos em log, nem controla exceções. Apenas controla eventos de negócios. E não se trata de interesses transversais, mas de uma decisão de arquitetura que se aplica principalmente ao armazenamento.

Definição de Event Sourcing

Em resumo, o ES serve para usar eventos como a principal fonte de dados. O ES não é necessariamente útil para qualquer aplicativo, por isso foi solenemente ignorado pelos desenvolvedores durante décadas. Se o ES parece inútil hoje, é bem possível que você ainda não tenha precisado dele.

Eu gostaria de resumir a necessidade de ES da seguinte forma: Para o especialista em domínio que precisa controlar a sequência de eventos que o software pode produzir, o Event Sourcing é uma opção viável. Em outros casos, os eventos ainda podem ser úteis para expressar fluxos de trabalho e concatenar partes da lógica de negócios. Nesse caso, no entanto, os eventos não são cidadãos de primeira classe no domínio e podem não ser persistentes. Este é o principal cenário hoje em dia.

Vamos ver o que fazer quando os eventos são a principal fonte de dados do seu aplicativo. O ES afeta dois aspectos de armazenamento: persistência e consultas. A persistência se caracteriza por três operações essenciais — Inserir, Atualizar e Excluir. Em um cenário de ES, Inserir é quase o mesmo que ocorre em um sistema clássico que persiste o estado atual da entidade. O sistema recebe uma solicitação e grava um novo evento no repositório. O evento contém um identificador exclusivo (por exemplo, um GUID), o nome do tipo ou um código que identifica o tipo de evento, um carimbo de data/hora e informações associadas.

Atualizar consiste em outro Inserir no mesmo contêiner de entidades de dados. A nova entrada simplesmente reflete os dados — quais propriedades foram alteradas, o novo valor e, se relevante no domínio corporativo, por que e como foram alteradas. Depois que uma atualização é executada, o armazenamento de dados evolui, conforme mostrado na Figura 2.

Um novo registro indica a atualização da entidade com ID #1
Figura 2 Um novo registro indica a atualização da entidade com ID #1

A operação Excluir funciona da mesma forma que a Atualizar, com a exceção de que contém informações diferentes para deixar claro que é uma exclusão.

Atualizar desta forma imediatamente apresenta alguns problemas quando se trata de consultas. Como saber se um registro específico existe ou qual é seu estado atual? Isso exige uma camada ad hoc para consultas, de forma a selecionar conceitualmente todos os registros com ID correspondente, depois analisar o conjunto de dados evento após evento.

Por exemplo: uma nova entidade de dados poderia ser criada com base no conteúdo do evento criado. Em seguida, todas as etapas sucessivas seriam reproduzidas e o que restasse no final do fluxo seria retornado. Esta técnica é conhecida como reprodução de evento. A simples reprodução de eventos para recompilar o estado pode gerar alguns preocupações sobre o desempenho.

Pense em uma conta bancária. Um cliente que abriu uma conta bancária anos atrás teria centenas de operações e eventos acumulados desde então. Para obter o saldo atual, seria preciso reproduzir centenas de operações para recompilar o estado atual da conta. Isso nem sempre é prático.

Há soluções alternativas para esse cenário. A mais importante consiste em criar instantâneos. Um instantâneo é um registro que salva o estado conhecido da entidade em determinado momento. Dessa forma, não é necessário para reproduzir eventos com data anterior aos instantâneos.

O ES não está associado a alguma tecnologia ou produto, seja um determinado banco de dados relacional ou um repositório de dados NoSQL. O ES necessita de pelo menos um componente de software especial — o repositório de eventos. O repositório de eventos é essencialmente um log de eventos. É possível criar um usando seu próprio código no topo da API de repositório de dados que você escolher.

Um repositório de eventos tem duas características principais. Em primeiro lugar, ele é um repositório de dados somente de acréscimo. Ele não oferece suporte a atualizações e, opcionalmente, só pode oferecer suporte a tipos específicos de exclusões. Em segundo lugar, um repositório de eventos deve ser capaz de retornar o fluxo de eventos associados a uma determinada chave. Você pode criar essa camada de código por conta própria ou usar estruturas e ferramentas disponíveis.

Opções de repositório de eventos

Você pode implementar um repositório de eventos usando qualquer coisa que funcione. Ele geralmente usa um banco de dados relacional ou algum tipo de repositório de dados NoSQL como mecanismo de persistência. Se você planeja usar um banco de dados relacional, é possível ter uma tabela por tipo de entidade que produza uma linha por evento.

Eventos geralmente têm layouts diferentes. Por exemplo, cada evento pode ter um número diferente de propriedades a serem salvas, o que torna difícil imaginar um esquema comum para todas as linhas. Se um esquema comum resultante da união de todas as colunas possíveis for viável e funcionar de forma aceitável, esta é uma opção fácil de implementar.

Caso contrário, você pode examinar o recurso de armazenamento de índice de coluna do SQL Server 2014, que configura a tabela para armazenar dados em colunas verticais em vez de linhas horizontais. Outra opção que funciona com qualquer versão do SQL Server é normalizar as propriedades do evento para um objeto JSON e armazená-los como uma cadeia de caracteres em uma única coluna.

No jargão do NoSQL, “documento” é um objeto com um número variável de propriedades. Alguns produtos NoSQL são especializados em armazenamento de documentos. Da perspectiva de um desenvolvedor, nada poderia ser mais fácil. Crie uma classe, preencha com valores e armazene como está. O tipo de classe é uma informação chave, que vincula vários eventos. Se usar NoSQL, será preciso ter apenas um objeto de evento e salvá-lo.

Projetos em andamento

O ES é uma abordagem de arquitetura relativamente recente. As ferramentas padrão que ajudam a escrever código no topo de repositórios de dados baseados em evento ainda estão surgindo. Definitivamente, é possível encontrar uma solução de ES para você, mas algumas ferramentas ad hoc podem ajudar a lidar com o armazenamento de eventos de maneira mais estruturada.

A principal vantagem de usar armazenamento de dados com reconhecimento de eventos é que a ferramenta, como um banco de dados, permite que você execute somente ações que leiam e anexem eventos de uma forma que garanta a consistência de negócios da abordagem de fonte de eventos. Uma estrutura especificamente desenvolvida para armazenar eventos é o projeto NEventStore (neventstore.org). O projeto permite gravar e reler eventos voltar e funciona de maneira independente da persistência. Veja como salvar um evento:

var store = Wireup.Init()
  .UsingSqlPersistence("connection")
  .InitializeStorageEngine()
  .UsingJsonSerialization()
  .Build();
var stream = store.CreateStream(aggregateId);
stream.Add(new EventMessage { Body = eventToSave });
stream.CommitChanges(aggregateId);

Para reler eventos, abra o fluxo e o loop por meio da coleção de eventos comprometidos.

O Event Store (geteventstore.com) é outro que funciona oferecendo uma API para HTTP simples e .NET para fluxos de eventos. No jargão de ES, uma agregação equivale a um fluxo no repositório. Você pode executar três operações básicas em um fluxo de eventos: gravar eventos, ler o último evento, um evento específico e até mesmo uma fatia de eventos, e se inscrever para receber atualizações.

Existem três tipos de assinatura. Uma é volátil, ou seja, quando um evento for gravado em determinado fluxo, uma função de retorno de chamada será invocada todas as vezes. Outra é de atualização, isto é, você receberá notificações para cada evento no repositório, começando no que for determinado e, depois disso, para qualquer evento recém-adicionado. Por fim, a assinatura persistente aborda o cenário em que vários consumidores estão aguardando o processamento de eventos. A assinatura garante que os eventos sejam entregues aos clientes pelo menos uma vez, mas possivelmente várias vezes e em ordem imprevisível.

Conclusão

O Event Sourcing usa eventos como fonte de dados do aplicativo. O aplicativo é arquitetado para salvar não o último estado conhecido das entidades, mas sim a lista de eventos de negócios relevantes. A fonte de dados de evento armazena dados em um nível baixo de abstração. É preciso aplicar projeções para chegar até o estado da entidade exigido para transações e consultas. A projeção é o processo de reproduzir eventos e executar algumas tarefas. A projeção mais óbvia é compilar o estado atual, mas você pode fazer projeções de eventos de qualquer tipo e em qualquer quantidade.


Dino Esposito é co-autor de “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2014) e “Programming ASP.NET MVC 5” (Microsoft Press, 2014). Um evangelista técnico para as plataformas Microsoft .NET Framework e Android na JetBrains e palestrante frequente em eventos do setor em todo mundo, Esposito compartilha sua visão do software em software2cents.wordpress.com e no Twitter em twitter.com/despos.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Jon Arne Saeteras