Como os comandos Adicionar / Criar * devem ser tratados na arquitetura CQRS + Event Sourcing

11

Quero implementar meu primeiro aplicativo usando o padrão CQRS junto com o Event Sourcing. Gostaria de saber como a criação de raízes agregadas deve ser tratada adequadamente. Digamos que alguém envie o comando CreateItem. Como deve ser tratado? Onde o evento ItemCreated deve ser armazenado? Como primeiro evento de um novo item? Ou devo ter algum tipo de entidade ItemList que agrega todos os itens e sua lista de eventos consiste apenas em eventos ItemCreated?

Udi Dahan sugere não criar raízes agregadas e sempre usar algum tipo de método de busca. Mas como posso buscar algo novo e certamente não tem nenhum ID atribuído. Eu entendo a idéia por trás e é bastante razoável pensar que um novo objeto é um objeto cujo estado é composto de zero eventos respondidos. Mas como devo usá-lo? Devo ter um método distinto no meu Repositório como getNewItem()ou fazer meu get(id)método aceitar Optional<ItemId>?

Edit: Depois de algum tempo de escavação, achei a implementação realmente interessante dos padrões acima mencionados usando atores. O autor, em vez de criar o agregado, o recupera de algum tipo de repositório com o UUID recém-criado. A desvantagem dessa abordagem é que ele permite um estado de inconsistência temporário. Também estou querendo saber como posso implementar o deletemétodo com essa abordagem. Basta adicionar o evento Excluído à lista de eventos do agregado?

Mequrel
fonte
1
Suspeito que o pós-título de Udi seja enganador. IMHO, parece que seu objetivo real é que os ARs recém-fabricados sempre estejam acessíveis de outro lugar, de uma maneira que capte o contexto sobre por que / como / quem decidiu que o novo AR precisava ser criado. Todo o resto é sobre como uma implementação específica (NHibernate?) Pode facilitar o gerenciamento.
Darien
2
Observe que o artigo de Udi Dahan que você está referenciando especificamente ressalta que seus conselhos podem não se aplicar à fonte de eventos: udidahan.com/2009/06/29/dont-create-aggregate-roots/…
EZ Hart

Respostas:

13

A idéia no post de Udi, como reconheço, é que nenhum tipo de item aparece do nada. Há (quase) sempre alguma coisa, ou mais especificamente, alguma operação de domínio, que causou a criação do item. Assim como o exemplo de Udi de um usuário realmente nascido de um visitante que se registra no site. Nesse ponto e nesse contexto limitado, Visitor é a raiz agregada, que é recuperada por seu endereço IP. Este Visitante cria o novo "item", um usuário neste momento, por meio de uma operação de domínio chamada Registro . O mesmo vale para a etapa anterior, que é outro contexto limitado: o referenciador é o AR, que é recuperado pela URL e que possui uma operação de domínio chamada BroughtVisitorWithIp , onde o visitante nasceu.

O Udi também escreve muito bem sobre exclusão: http://www.udidahan.com/2009/09/01/dont-delete-just-dont/ . A idéia principal é que você nunca exclua nada. Há sempre uma operação de domínio por trás, que queremos capturar. Como um pedido sendo cancelado, em vez de excluído. Leia, é um post muito bom.

O ponto principal aqui em ambas as contas, executando DDD e, especialmente, Event Sourcing, é que você nunca deve fazer operações CRUD diretas. Se você se encontrar em uma situação em que realmente precisa apenas inserir, atualizar ou excluir alguns dados, e realmente não há nenhuma operação de domínio por trás disso, talvez o DDD e o Event Sourcing não sejam adequados para esse contexto limitado . Você é livre para combinar esses dois conforme desejar, desde que um único contexto delimitado adira a um princípio. Dessa forma, o contexto limitado no estilo CRUD pode criar alguma linha no banco de dados, que se torna uma entidade e uma raiz Agregada em outro contexto limitado, onde agora você pode recuperar o AR e não precisar criá-lo.

Tuukka Haapaniemi
fonte
2
"talvez o DDD e o Event Sourcing não sejam adequados para esse contexto limitado". Você entende direito o DDD. Não deve ser implementado em todos os casos apenas para a glória de satanás, mas apenas quando é necessário lidar com um domínio complexo e cheio de regras incertas. Pessoalmente, eu fiz isso para software jurídico em que os requisitos não são conduzidos pela lógica.
Yegor Chumakov
2
+1 apenas nesta frase "para esse contexto limitado". :)
Songo 03/07
2
Em +1, o uso dos verbos 'Adicionar' e 'Criar' é altamente sugestivo de que você ainda está pensando em seu domínio em termos de interação com um bom e antigo banco de dados tabular. Sem conhecer seu domínio / contexto limitado, não posso dizer se isso é apropriado ou não. Ignore a persistência, concentre-se primeiro nos COMANDOS e EVENTOS (também conhecidos como INTENÇÕES e RESULTADOS), que são exclusivos do seu domínio, e depois se preocupe com a persistência do estado, que é um problema que já foi resolvido centenas de milhares de vezes antes.
Matt
"o uso dos verbos 'Adicionar' e 'Criar' é altamente sugestivo de que você ainda está pensando em seu domínio em termos de interação com um bom e antigo banco de dados tabular" Hmmm. Quando você tem um design de interface do usuário com um grande botão 'Adicionar algo', então, infelizmente, essa é a intenção; literalmente para adicionar algo novo. Eu geralmente concordo com você, mas não estamos falando no nível do banco de dados aqui; às vezes, Adicionar ou Criar são, na verdade, as palavras certas para usar.
Designer # #
1
@designermonkey Quando você tem esses botões na interface do usuário, você realmente tem uma operação de domínio por trás deles? Talvez, mas 9 em 10 vezes não haja realmente uma operação de domínio complexa nesse contexto limitado. E a operação CRUD pura é apenas isso, uma operação CRUD pura e deve ser tratada como tal. Somente quando houver necessidade da complexidade do modelo de domínio, ele deve ser usado. Assim, os diferentes contextos delimitados com diferentes princípios de design.
Tuukka Haapaniemi