3 CQRS - Command and Query Responsibility Segregation
3.4 Event sourcing e Dados imutáveis
3.4.3 Command Handlers
Um command handler é um processo responsável por receber comandos (normalmente através de uma queue) e aplicá-los nas respetivas entidades. Todos os comandos têm um recetor associado e por isso a aplicação de um comando passa por três fases: numa primeira fase é necessário carregar a respetiva entidade à qual se irá aplicar o comando de forma única, que poderá já estar em memória; numa segunda fase um comando passa pela sua aplicação no comando da entidade, ou seja, invoca um determinado método para a alteração; por fim, numa terceira fase e caso tenham sucesso é necessário comunicar à lista de comandos que aquele já foi processado (na maioria das vezes é retirar da lista da queue) e fazer persistir todas as alterações do próprio comando.
As alterações podem acontecer de duas formas no sistema, através de event sourcing, ou seja, guarda-se todo o detalhe da alteração (guardar as alterações de domínio é primordial) para posteriormente poder ser reprocessado se for necessário ou então state persistence (alteração direta do estado do sistema mas pouco utilizada), ou seja, não existe a persistência para algum tipo de log da alteração que vai ser feita no sistema sendo logo feita no domínio.
Nos dois casos, é necessário manter a persistência dos dados e para isso é preciso garantir
50
que o comando tem sempre o mesmo resultado, ou seja, para qualquer número de execuções do mesmo comando o resultado terá de ser sempre o mesmo, podendo na mesma acontecer situações como mensagens duplicadas (muito em cloud ou sistemas particionados) ou o processo bloquear depois de ter feito as alterações, mas antes de dar o ACK do processamento da mensagem atual. Logo, quando existe alteração de dados utilizando event sourcing, simplesmente são publicados todos os eventos dando conhecimento às partes interessadas do sistema, das últimas alterações (Abdullin, 2010a).
Um conceito associado aos command handlers é o de subscrições de eventos sendo estas, muitas vezes usadas quando aplicadas a uma arquitetura CQRS. São caracterizadas por serem publicadas por entidades interessadas num determinado evento. Cada entidade é algo que pode ser identificado de forma única em todo o sistema e que se situa num command handler, como Aggregate Root, Sagas, modelo de leitura; A entidade publica a sua lista de queries de eventos, ou seja, as queries são públicas e podem ser replicadas para todas as partições, de forma a se poder escalar facilmente; as subscrições são uma forma de comunicar o interesse para um determinado tipo de mensagens e que a partir daquele momento quer receber tudo o que diz respeito àquele tipo de mensagens; alguns exemplos serão eventos de timeouts e de domínio.
Em CQRS existe um termo denominado de Aggregate Root, e que é usado em muitos sistemas.
De forma a se perceber melhor e fazendo um paralelismo com a programação orientada a objetos, em que uns objetos têm referências para outros objetos, por exemplo, o Cliente tem referência para uma Encomenda, mas os AR são um pouco diferentes. Os AR promovem uma melhor separação, ou seja, enquanto o Cliente e Encomenda podem existir independentes no sistema, algumas entidades e respetivos valores já não fazem sentido serem separados, como é o caso da Encomenda e LinhaEncomenda. Neste caso a LinhaEncomenda já não faz sentido existir sem Encomenda nem fazer parte de outra Encomenda, e por isso LinhaEncomenda possivelmente será um Aggregate e Encomenda um Aggregate Root. Para ajudar neste raciocínio, bastará pensar no Cascading Delete de uma determinada entidade e verificar se faz sentido que ao apagar uma entidade outra relacionada também seja apagada. Por exemplo, se se desejar apagar a Encomenda deverá apagar-se também as LinhaEncomenda, porém, este pensamento já não é válido para a relação entre Cliente e Encomenda (Charlton, 2009).
Existe um tipo de entidade, muito falado no mundo do CQRS denominado Saga que também pode fazer parte de um command handler, sendo caraterizado por ser um message handler a correr no servidor, com a função de ajudar a gerir transações de negócio muito grandes, conseguindo gerir o passar do tempo com gestão de timeouts. Permite também proporcionar consistência nos dados mas não pode ir além da entidade em que se encontra (AR - Aggregate Root), daí, a um sistema deste tipo poderem na mesma chegar mensagens duplicadas ou fora de ordem.
As Sagas podem-se ligar às subscrições de eventos e receber comandos diretamente, e por isso muitas vezes são confundidas mesmo com os command handlers. Porém existem algumas
3.4 Event sourcing e Dados imutáveis
51 diferenças entre as sagas e os tradicionais Aggregate Root nos command handlers que são:
as funções de negócio, e as intenções do domínio.
Quanto às funções de negócio, os AR são responsáveis pelo house-keeping enquanto as sagas refletem as especificações todas do negócio como a forma de lidar com a inconsistência dos dados e probabilidades existentes;
Já no que se refere às intenções do domínio, os AR agem em limites contextuais, enquanto as sagas interagem entre eles, com o tempo e com o inesperado;
Tecnicamente, as sagas subscrevem eventos e recebem os comandos diretamente, enquanto os command handlers apenas recebem comandos.
Com a ajuda da Imagem 20, é possível perceber melhor a ligação entre estes conceitos, e compreender todo o fluxo (Abdullin, 2010g).
Imagem 20 – Command handling, Sagas, Event Subscription (Abdullin, 2010g)
52
Aqui, vemos como se relacionam os três conceitos, sendo que na realidade o cenário poderá ser diferente (mais complexo), sendo este apenas a relação base que deverá estar sempre presente em qualquer sistema que os use. Com isto podemos tornar os sistemas mais escaláveis, ricos em informação, confiáveis se usarmos event sourcing e message dispatcher, redundantes, e com permissão de reordenação de mensagens para cenários de cloud. De notar que nestes cenários, é necessário saber a versão com que se está a trabalhar para dessa forma, diminuir os problemas de concorrência (Abdullin, 2010g).
Na Imagem 21 já a seguir, fica demonstrado e em detalhe, como ficará a integração de comandos num sistema com eventos.
Imagem 21 - Arquitetura de CQRS (Ashton, 2011)
Com a Imagem 21 podemos ver que cada comando é consumido sempre por um Command Handler através do command bus (zona 2) e os seus nomes devem ser sempre imperativos, como por exemplo “CancelOrder”. Cada Command Handler converte o comando numa chamada a funções num Aggregate Root (AR) (zona 3), que por sua vez contém eventos públicos (Fossmo, 2009). Cada evento representa uma mudança de estado no domínio, e todos eles são registados num repositório para posterior consulta. Após esse registo são consumidos por Event Handlers (zona 4) que devem ter sempre os seus nomes no passado, por exemplo, “OrderCanceled”. Cada event handler aplica a mudança de estado ao respetivo destino, por exemplo, o view model, que é a representação dos dados na UI (Tom, 2010).
3.4 Event sourcing e Dados imutáveis
53 No próximo subcapítulo será apresentada a comunicação entre modelos e componentes da arquitetura, de forma detalhada.