Como lidar com erros de pós-validação no comando (DDD + CQRS)

18

Por exemplo, quando você enviar um formulário Register, você tem que verificar no Domain Model( WriteModelem CQRS) que ele está em um estado válido (exemplo, email sintaxe de endereço, idade, etc).

Então você cria um Commande envia para a Command Bus.

Eu entendo que os comandos não devem retornar nada.

Então, como você lida com um erro além Command Bus? (Por exemplo, um usuário registrou 1 segundo antes com o mesmo username/email).

Como você sabe que o comando falhou e como você sabe o erro?

JorgeeFG
fonte
2
Você não precisa de um barramento de eventos. Por que todo mundo acha que você precisa de um barramento de eventos ao implementar o CQRS, eu simplesmente não sei. O CQRS simplesmente significa que você separa completamente as gravações e leituras, faz as preocupações separadas. Você tem CQRS desde que faça isso. Seus comandos nem precisam ser assíncronos. Se comandos síncronos com relatório de erros funcionarem para você, faça isso.
Andy
1
Os comandos não devem retornar nada quando forem bem-sucedidos . A idéia partiu do CQS , que teve a implicação de que o comando ainda poderia gerar uma exceção por erro. O que você está descrevendo é um comando unidirecional. Veja esta resposta em relação a isso.
precisa

Respostas:

4

Eu entendo que os comandos não devem retornar nada.

Essa é uma visão, mas não está totalmente imutável. Considere gravações (PUT, POST, DELETE) em HTTP - todas essas mensagens são comandos, no sentido de que são mensagens com solicitação de mudança de estado do recurso e, no entanto, todas elas retornam respostas.

Então, como você lida com um erro além do Command Bus? (Por exemplo, um usuário se registrou 1 segundo antes com o mesmo nome de usuário / email).

Como você sabe que o comando falhou e como você sabe o erro?

Portanto, em um caso em que você está se comunicando diretamente com o manipulador de comandos, uma mensagem retornada é uma maneira perfeitamente razoável de reconhecer que o comando foi recebido e processado.

Se você estiver usando um middleware, como um barramento, que o impede de se comunicar diretamente com o destino, sugiro que você procure padrões de mensagens assíncronos - como você faz com que o manipulador de comandos envie uma mensagem de volta ao chamador?

Uma idéia é assinar o resultado do comando; isso se baseia em algumas das idéias dos padrões de integração corporativa da Hohpe. A idéia básica é que, como o cliente está familiarizado com a mensagem de comando enviada, está bem posicionado para assinar quaisquer novas mensagens publicadas como conseqüência da mensagem de comando. O manipulador de comandos, depois de salvar os dados no livro de registro, publica eventos anunciando que a alteração foi bem-sucedida e o cliente se inscreve nesses eventos - reconhecendo os eventos corretos considerando a coincidência de vários identificadores na mensagem (ID de causa, ID de correlação e assim por diante).

Abordagens alternativas são um pouco mais diretas. Um seria incluir na mensagem um retorno de chamada, que pode ser chamado pelo manipulador de comandos depois que a mensagem for tratada com êxito.

Uma alternativa muito semelhante é reservar espaço na mensagem de comando para o manipulador de comandos escrever a confirmação - como o cliente já possui a mensagem de comando em questão, o circuito já está completo. Pense em " promessa " ou " futuro completável". A mensagem informa ao manipulador de comandos onde escrever a confirmação; isso sinaliza ao cliente (trava de contagem regressiva) que a confirmação está disponível.

E, claro, você tem a opção adicional de remover o middleware que parece estar atrapalhando a maneira correta de fazer a coisa certa.

Por exemplo, um usuário registrou 1 segundo antes com o mesmo nome de usuário / email

Se você estiver manipulando o registro do usuário de forma idônea, isso não seria necessariamente um erro - repetir mensagens até que uma resposta seja observada é uma maneira comum de garantir pelo menos uma vez a entrega.

VoiceOfUnreason
fonte
2

Por exemplo, quando você envia um formulário de registro, é necessário verificar no modelo de domínio (WriteModel no CQRS) se ele está em um estado válido (exemplo, sintaxe do endereço de email, idade etc.)

Existem muitos tipos de validação. A validação quando você verifica a sintaxe do endereço de email e o formato de idade é um tipo de validação que um Comando pode fazer. Esta não é realmente uma preocupação do domínio. Pode parecer assim porque alguns especialistas em domínio informam essas especificações, mas você deve fazer esse tipo de validação na criação do comando. De fato, a idéia geral é fazer a validação o mais rápido possível, porque depois que um comando é criado e enviado para um BUS, é mais complicado executar ações.

Então, como você lida com um erro além do Command Bus? (Por exemplo, um usuário se registrou 1 segundo antes com o mesmo nome de usuário / email).

Esse tipo de validação é discutido muito na comunidade do CQRS, desde o início do CQRS. Onde fazer isso? É muito debatido. Pessoalmente, uso a seguinte abordagem: Antes de o comando ser enviado ao BUS, março o nome de usuário / email como tomado de maneira centralizada (isto é, uma restrição de índice exclusiva no nome de usuário / email). Depois disso eu envio o comando. A desvantagem é que, se o comando falhar, por um curto período de tempo, esse nome de usuário será usado e não usado; isso é aceitável para os meus negócios, provável para os seus negócios.

Como você sabe que o comando falhou e como você sabe o erro?

Se um comando assíncrono for rejeitado pelo Agregado por causa de um domínio invariável, algumas medidas deverão ser tomadas com a emissão de algum comando compensatório que notifique de alguma forma o emissor do comando (por exemplo, envie um email de explicação de que o comando falhou).

O problema com o email duplicado é que você não pode enviar um email porque esse endereço de email pertence a outra pessoa, é por isso que eu o checo antes de enviar o comando para o barramento.

Constantin Galbenu
fonte
1

A validação deve ser realizada em um decorador. Qualquer comando que precise de validação pode ser decorado como tal.

As validações podem ser tratadas com exceções se o retorno for anulado com o seu comando, para que possam ser atendidas com chamadas de sincronização ou assíncrona com o resultado da tarefa retornada.

Outra possibilidade é pensar na validação como um tipo de "consulta" que retornaria um resultado de validação. Execute a consulta de validação e, se aprovada, execute o comando Isso seria uma alternativa à abordagem do decorador.

Jon Raynor
fonte
Eu gosto da abordagem de exceção porque é a maneira mais limpa. Mas as exceções não são muito pesadas para isso?
EresDev 12/12/19
1
HI @EresDev - Sim, eles são pesados. Mas não sei o quão "válidos" são seus dados. Digamos que de 1000 registros 2 não sejam válidos. Exceções podem ser viáveis, pois são condições verdadeiramente excepcionais. Agora considere 1000 registros com 200 inválidos. Por essa causa, sim, as exceções devem ser evitadas porque 20% dos seus dados não são válidos. Portanto, deixo a você decidir se deve ou não escolher essa abordagem com base na sua validade atual dos dados. :)
Jon Raynor
Acrescentarei a isso dizendo que, se mantivermos o exemplo de formulário de registro, onde acho que 50% ou mais usuários inserem dados inválidos primeiro, a exceção ainda está correta. No back-end de aplicativos da web, você geralmente obtém dados válidos porque os front-ends também estão executando a validação. Você só obtém se algo tiver perdido a validação do frontend por alguma chance rara ou se alguém estiver brincando. Portanto, as exceções são muito boas neste caso.
EresDev 12/12/19