UserAggregate user = mUsersRepository.getUser(userId);
At this point I become confused.
Sim, isso é um pouco complicado.
However, if multiple UserAggregate objects will be used simultaneously, these objects might get out of sync with each other (while staying consistent individually). I get a feeling that having several "non-synchronized" instances of the same aggregate can lead to all kinds of nasty bugs, but I can't really be sure about it.
Existem duas preocupações.
A primeira é que as operações que lêem o estado do agregado podem estar lendo uma cópia antiga. Na maior parte, a literatura reconhece que isso simplesmente significa que as leituras são "eventualmente consistentes". É análogo a trabalhar com recursos mutáveis na Web - o Alice POST é uma alteração no recurso, mas Bob não vê essa alteração imediatamente porque ele tem uma cópia em cache.
O padrão CQRS chega ao ponto de levar o suporte para consultas (leituras) completamente fora do modelo - o O papel de suas raízes agregadas é garantir que a consistência seja mantida durante as gravações.
A segunda é que, se tivermos vários escritores (dois threads, possivelmente em processos diferentes, possivelmente em hosts diferentes) executando um write ao mesmo tempo , podemos acabar com um cérebro partido; "o" agregado em dois estados diferentes, dependendo de onde você olha, e podemos perder as edições quando um é salvo em cima do outro.
Esse é o problema que é "real" e precisa ser abordado.
Uma resposta é restringir-se a um único escritor. A arquitetura LMAX é um exemplo disso - todos os comandos são linearizados, publicando-os em uma fila, e "o" escritor então consome a fila e gerencia todas as gravações. Então, você resolve o problema dos conflitos não tendo nenhum - ou, mais corretamente, aceita um problema de eleição do líder em troca do problema de conflito.
Com vários escritores; A resposta usual é reconhecer que o banco de dados (seja um RDBMS ou uma coisa sem SQL ou um sistema de arquivos) é o livro de registros - é o que você vai usar como "verdade" se a energia for desligada. . Portanto, seus escritores estão compartilhando o banco de dados e você precisa impor a integridade da gravação nele.
Eu só sei de duas respostas - seus escritores correm para pegar uma fechadura, e o vencedor ganha autoridade de escrita até que a fechadura seja liberada, ou seus escritores correm para comparar e trocar.
If I understand it correctly, each aggregate gets a version associated with it. On each update of aggregate's state the version is checked, and, if the version in DB is not the same as the version of the cached aggregate, the update fails.
Isso é "compare e troque", pelo menos logicamente. Você pode precisar jogar jogos com números de versão, ou posições de fluxo, ou algo parecido, para obter uma implementação funcional, mas logicamente eles são variações de "alterar o estado atual daquele localizado lá para aquele localizado aqui ". PUT condicional, por assim dizer.
However, it feels like a lot of work - each method that updates the aggregate can fail, therefore the code that uses it must be capable of retrying the operation.
Aqui estão as notícias "boas" - em um sistema distribuído, você precisava se preocupar com o problema mesmo assim . O banco de dados pode não estar disponível, pode estar sobrecarregado, o sistema de arquivos pode estar cheio etc. É um trabalho, mas não é um trabalho extra .
Is there a standard approach that DDD community uses?
A maioria das pessoas que estão fazendo o sourcing de eventos tendem a usar comparar e trocar, especialmente quando usam armazenamentos de eventos NoSql que suportam gravações idempotentes.
Mas meu sentimento é que ES ainda é uma minoria relativa no espaço; é menos claro para mim qual abordagem as pessoas salvando o estado agregado estão usando.