Estou projetando um sistema que usa Event Sourcing, CQRS e microsserviços. Sou levado a entender que esse não é um padrão incomum. Um recurso essencial do serviço precisa ser a capacidade de reidratar / restaurar de um sistema de registro. Os microsserviços produzirão comandos e consultas em um MQ (Kafka). Outros microsserviços responderão (eventos). Comandos e consultas serão mantidos no S3 para fins de auditoria e restauração.
O processo atual de pensamento era que, para fins de restauração do sistema, poderíamos extrair o log de eventos do S3 e simplesmente enviá-lo ao Kafka.
No entanto, isso não reconhece mudanças nos produtores e consumidores ao longo do tempo. O controle de versão no nível de comando / consulta parece ajudar bastante a solucionar o problema, mas não consigo entender os consumidores de controle de versão, de modo que eu possa impor que, quando um comando, durante uma restauração, é recebido e processado, é exatamente o mesmo [versão do] código que está executando o processamento, pois foi a primeira vez que o comando foi recebido.
Existem padrões que posso usar para resolver isso? Alguém conhece outros sistemas que anunciam esse recurso?
EDIT: Adicionando um exemplo.
Um 'comprador' envia uma 'pergunta' a um 'vendedor' no meu site de leilões. O fluxo tem a seguinte aparência:
UI -> Web App: POST /question {:text text :to seller-id :from user-id}
Web App -> MQ: SEND {:command send-question :args [text seller-id user-id]}
MQ -< Audit: <command + args appended to log in S3>
MQ -< Questions service: - Record question in DB
- Email seller 'You have a question'
Agora, como resultado de um novo requisito comercial, ajusto o consumidor do 'Serviço de perguntas' para persistir uma contagem de todas as perguntas não lidas. O esquema do banco de dados foi alterado. Até o momento, não tínhamos noção se uma pergunta foi lida ou não pelo vendedor. A última linha se torna:
MQ -< Questions service: - Record question in DB
- Email seller 'You have a question'
- Increment 'unread questions count'
Dois comandos são problemas, um antes da alteração, um após a alteração. A 'contagem de perguntas não lidas' é igual a 1.
O sistema trava. Restauramos repetindo os comandos através do novo código. No final da restauração, nossas 'perguntas não lidas contam' são iguais a 2. Mesmo que, neste exemplo artificial, o resultado não seja uma catástrofe, o estado que foi restaurado não é o que era anteriormente.
fonte
Respostas:
Primeiro, é importante entender e poder aproveitar a diferença entre comandos e eventos.
Como esta pergunta aponta sucintamente, Comandos são coisas que gostaríamos que acontecesse e Eventos são coisas que já aconteceram. Um comando não resulta necessariamente em um evento significativo no sistema, mas geralmente ocorre. Por exemplo, um
send message
comando pode ser rejeitado; nesse caso, nenhum evento acontece (normalmente, um erro não seria considerado um evento nesse sentido, embora ainda possamos optar por registrá-lo em um log de diagnóstico). Agora, se osend message
comando for aceito, omessage sent
evento ocorrerá e os detalhes do evento poderão descrever o remetente, o destinatário e o conteúdo.Quando falamos sobre o estado do sistema, na verdade estamos discutindo não um ponto culminante de comandos, mas de eventos. Somente eventos podem causar uma mudança de estado no sistema. Para tirar um exemplo da vida, suponha que eu vá ao supermercado Publix local e compre um bilhete de loteria da Flórida. O comando foi "Comprar ticket" e o evento foi "Ticket emitido". Meu próximo comando, então, é na loteria desenhar meus números para o PowerBall. A loteria ignorará meu comando (mas não tenho conhecimento), e o evento "Números de PowerBall escolhidos" ocorrerá independentemente dos meus desejos. Se meus números coincidirem, o evento "Jackpot ganho" acontece comigo (e acho que meu comando foi ouvido). Caso contrário, percebo que meu comando foi ignorado.
De uma perspectiva histórica, a loteria está interessada apenas em um subconjunto de eventos. A loteria apenas se preocupa com a (a) emissão de um bilhete, (b) os números foram escolhidos e (c) o jackpot foi ganho. Esses são os itens de interesse. O ato de comprar o ingresso, querer ganhar etc. é irrelevante, como é o que faço com o ingresso depois que perco. Enquanto o mundo real muda para eventos mundanos, precisamos apenas registrar os eventos que são significativos para o nosso sistema.
Em teoria, sob uma técnica de fornecimento de eventos, um fluxo de eventos pode ser repetido desde o início dos tempos para chegar ao estado atual. Isso se baseia na suposição de que as condições subjacentes do sistema são constantes e determinísticas. No entanto, essas suposições não são válidas em muitos sistemas. Os dados associados a um evento, bem como os tipos de eventos nos quais estamos interessados, podem mudar à medida que nosso software de computador evolui. Além disso, pode ser dispendioso computacionalmente recalcular o estado atual em resposta a todas as consultas. Por esse motivo, as capturas instantâneas do estado do sistema geralmente são feitas para representar pontos conhecidos no tempo, aos quais os eventos mais recentes podem ser adicionados.
Embora ainda seja possível reproduzir um fluxo de eventos em várias versões, a quantidade de esforço humano envolvido para fazer isso provavelmente será proibitiva de custos. A menos que haja uma razão justificável para projetar essa capacidade no sistema, é melhor criar seu sistema para utilizar snapshots.
Exemplo em questão
No exemplo dado na pergunta, a arquitetura não é verdadeiramente baseada em eventos; é baseado em comandos. A reprodução de comandos cria o estado do sistema. Este é um anti-padrão e deve ser corrigido. Em vez disso, os eventos principais são:
Cada um desses eventos pode ser "repetido" para fornecer o estado atual. Por exemplo, no ato de fazer uma pergunta, o comportamento do sistema pode ser enviar um email ao vendedor e aumentar o
unanswered question
contador. Esse comportamento pode ser alterado; no entanto, o fato de a pergunta ter sido feita não. Da mesma forma, o sistema pode diminuir ounanswered question
contador quando o vendedor responder. Esse comportamento é mutável, mas o fato de o vendedor responder não é.A maioria dos sistemas de fornecimento de eventos computa dinamicamente a contagem de perguntas não respondidas, reproduzindo o fluxo de eventos específico em resposta a uma consulta.
fonte
Para auditoria, com certeza. Para restaurar ? Isso é estranho e pode causar dores de cabeça.
Se você deseja obter fontes de eventos, deseja reidratar o estado de eventos (coisas que aconteceram no passado) e não de comandos. Isso evita a maioria dos problemas associados às alterações na implementação de comandos - você só precisa lidar com as alterações de estado persistentes.
O controle de versão ainda é uma preocupação. Em particular, você deseja garantir que seus eventos persistentes sejam o mais flexível possível (representações de DTOs, em vez de serializações diretas dos conceitos em seu domínio). Ao ler eventos da loja, você tem a oportunidade de atualizá-los conforme necessário antes de aplicá-los ao estado de reidratação.
fonte