Como devemos criar novas raízes agregadas na arquitetura cqrs? Neste exemplo, eu quero criar um novo AR2 raiz agregado que contém referência ao primeiro AR1.
Estou criando o AR2 usando o método AR1 como ponto de partida. Até agora, vejo algumas opções:
- Método interno no AR1
createAr2RootOpt1
eu poderia chamarnew AR2()
e salvar este objeto no db imediatamente usando o serviço de domínio que tem acesso ao repositório. Eu poderia emitir evento na primeira raiz agregada, por exemplo.
SholdCreateAR2Event
e depois temos uma saga sem estado que reage a isso e emite um comandoCreateAR2Command
que é então tratado e realmente cria AR2 e emiteAR2CreatedEvent
. No caso de usar a fonte de eventosSholdCreateAR2Event
, não seria preservado no armazenamento de eventos, pois não afeta o estado da primeira raiz agregada. (Ou ainda devemos salvar isso na loja de eventos?)class AR1{ Integer id; DomainService ds; //OPTION 1 void createAr2RootOpt1(){ AR2 ar2 = new AR2(); ds.saveToRepo(ar2); } //OPTION 2 void createAr2RootOpt2(){ publishEvent(new SholdCreateAR2Event()); //we don't need this event. Shoud it still be preserved in event store? } } class AR2{ Integer id; Integer ar1Id; void handle(CreateAR2Command command){ //init this AR with values and save publishEvent(AR2CreatedEvent()); //used for projections afterwards and saved inside AR2 event store } } class Saga{ void handle(SholdCreateAR2Event ev){ emitCommand(new CreateAR2Command()); } }
Qual é a maneira mais adequada de fazer isso?
fonte
AR1WasCreated
? Deveria serAR2WasCreated
? Além disso, se eu usar sua lógica, emito um eventoAR2WasCreated
antes que ele seja realmente criado? E salvar esse evento dentro do log de eventos do AR1 parece problemático, pois eu realmente não preciso desses dados dentro do AR1 (ele não modifica nada dentro do AR1).AR1WasCreated
-> SAGA (tem regra se A1 foi criado, então crie A2) ->CreateAR2Command
->AR2WasCreated
.Os padrões de criação são estranhos .
Udi Dahan tem algumas coisas úteis a dizer sobre o problema geral: Não crie raízes agregadas . O ponto básico é que o agregado não aparece do nada e que existe uma linguagem de domínio que descreve como eles aparecem, que devem ser capturados no seu modelo de domínio.
A tendência de distorção é que a entidade em seu modelo de domínio que está processando o comando não é a entidade que está sendo modificada pela transação. Isso não está errado; é apenas estranho (comparado aos casos em que você solicita que uma entidade se modifique.
Sua segunda abordagem também é boa. "Os eventos que criamos sem realmente salvar no banco de dados" são chamados de "eventos do domínio"
A ideia básica é que, dentro da mesma transação, o manipulador de comandos gere o evento, que viaja ao longo do barramento para um manipulador de eventos que permite que o segundo agregado se crie. Você obtém uma coesão de código um pouco melhor, talvez.
Nota: nos sistemas de origem de eventos, você geralmente não usa eventos dessa maneira.
Nota: os nomes dos eventos geralmente estão no tempo passado - ShouldCrateAR2 tem a ortografia incorreta.
Sim, se você estiver apenas lançando um evento no barramento síncrono para executar o código remoto, não deverá salvar esse evento no livro de registro. É apenas um detalhe de implementação nessa escala.
Evite modificar dois fluxos de eventos diferentes na mesma transação. Se essa criação também representar uma alteração no AR1, a resposta usual seria modificar o AR1 nessa transação, com um assinante assíncrono para os eventos responsáveis por disparar o comando para criar o AR2.
Manipulação de comando idempotente ajuda muito aqui.
fonte
AR1.doSmthn(AR2 param)
pois qualquer projeção de leitura que eu criar não possui dados completos que eu preciso (apenas o AR2 possui dados completos).