Como lidar com efeitos colaterais no CRQS ao reproduzir eventos?


10

Dizem que no CQRS é fácil corrigir um erro, você apenas reimplementa e reproduz os eventos.

Mas, e se um dos eventos fizer com que um sistema externo que não esteja sob seu controle "envie um item" para o cliente se você apenas reproduzir os eventos, o item será enviado duas vezes.

Como você resolve isso?

Respostas:


6

Você precisa fazer uma separação clara entre eventos que modificam o estado do seu modelo de leitura e eventos (potencialmente) que modificam o estado dos sistemas externos. Verifique se você não possui nenhum "evento misto" que modifique os dois estados juntos. Dessa forma, você pode reproduzir seus eventos em um "modo de reprodução" específico em que esses eventos para o sistema externo não são acionados novamente. Nesse modo, você também "simula" quaisquer eventos que foram originalmente iniciados pelo sistema externo (agora você os retira da fila de repetição).

Não se esqueça, a etapa de reimplantação significa na verdade redefinir o estado do modelo de leitura para um momento anterior. Provavelmente, isso não é nada que você possa fazer (ou precise fazer) pelo estado dos sistemas externos.


"Não se esqueça, a etapa de reimplantação significa na verdade redefinir o estado do modelo de leitura para um momento anterior. Provavelmente, isso não é o que você pode fazer (ou precisa fazer) pelo estado dos sistemas externos". -> mas e se eu quiser que minha reprodução tente novamente as chamadas de sistema externas com falha, como remessa? nesse caso, minha reimplantação de uma repetição não apenas redefiniria o estado do modelo de leitura, mas também causaria eventos externos, isso parece justo ou estou faltando alguma coisa?
Jas

2
@ Jas: você não deseja abusar da "repetição" para tentar novamente uma falha na chamada do sistema externo. Você usa o "replay" para obter o modelo de leitura do seu próprio sistema no mesmo estado em que estava antes. Isso significa que, no caso de uma falha na solicitação de remessa, seu sistema foi informado antes sobre essa falha e armazenou essas informações em algum lugar do estado. A reprodução garante que essas informações ainda estejam disponíveis após a "reimplantação e reprodução". Portanto, após a reprodução, seu sistema pode aplicar uma estratégia de "repetir remessa em caso de falha" (que não tem nada a ver com o CQRS, qualquer sistema robusto de pedidos deve ter essa estratégia).
Doc Brown

Interessante, era isso que eu tinha em mente: estava me perguntando se existe um "padrão" nisso, para não reinventar a roda!
Jas

3

Do artigo de Martin Fowler sobre fornecimento de eventos :

A idéia fundamental do Event Sourcing é garantir que todas as alterações no estado de um aplicativo sejam capturadas em um objeto de evento e que esses objetos de evento sejam armazenados na sequência em que foram aplicados pelo mesmo tempo de vida útil do estado do aplicativo.

Portanto, quando você precisa restaurar o estado do seu sistema para um determinado momento, reproduz o estado armazenado , não os manipuladores de eventos, até aquele momento.

Dito isto, se você estiver trabalhando apenas com dados do estado, não deverá haver efeitos no sistema externo. A menos que você tenha gatilhos ou observadores em seu armazenamento de eventos, nesse caso, você deve desativá-los pela duração da restauração. Como você diz que não tem controle sobre o sistema externo, não deve haver nenhuma tentativa de restaurar seu estado usando a API exposta, pois você não sabe quais efeitos colaterais podem ter no sistema deles. Se a restauração colocar o sistema em um estado intermediário (por exemplo, devido a falhas nas operações no sistema externo), isso não deverá ser da responsabilidade de uma repetição de evento.


2

Mas, e se um dos eventos fizer com que um sistema externo que não esteja sob seu controle "envie um item" para o cliente se você apenas reproduzir os eventos, o item será enviado duas vezes.

Para escolher um exemplo específico, vamos considerar como uma abordagem "pelo menos uma vez" aos efeitos colaterais pode funcionar.

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

Portanto, o modelo de domínio rastreia o que precisa ser feito; mas deixa o real fazendo para o aplicativo

No contexto da execução de um comando, a ideia básica parece a mesma. Os efeitos colaterais reais ocorrem fora da transação que atualiza o modelo.

Portanto, os testes de unidade para o seu modelo podem parecer algo como

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

Os principais pontos aqui sendo

  • A atualização do modelo é livre de efeitos colaterais; os efeitos colaterais reais ocorrem fora da transação que atualiza o modelo.
  • Um evento que descreve o resultado do efeito colateral precisa voltar.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.