Como criar uma nova raiz agregada no CQRS?


10

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:

  1. Método interno no AR1 createAr2RootOpt1eu poderia chamar new AR2()e salvar este objeto no db imediatamente usando o serviço de domínio que tem acesso ao repositório.
  2. Eu poderia emitir evento na primeira raiz agregada, por exemplo. SholdCreateAR2Evente depois temos uma saga sem estado que reage a isso e emite um comando CreateAR2Commandque é então tratado e realmente cria AR2 e emite AR2CreatedEvent. No caso de usar a fonte de eventos SholdCreateAR2Event, 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?

Respostas:


2

Eu acho que essa opção não. 2 é a solução, com uma modificação pequena, mas importante: AR1não deve emitir um evento cujo objetivo é criar o AR2, mas deve emitir um AR1WasCreatedevento. Esse evento deve persistir no armazenamento de eventos, pois é um evento importante que marca o nascimento de AR1. Em seguida, um Sagalistent Whould para AR1WasCreatedeventos e gerar um comando para criar AR2: CreateAR2Command.

A opção no.1 está muito errada. Você nunca deve injetar esse tipo de serviço de domínio em um Aggregate. Aggregatesdeve ser puro, sem efeitos colaterais que não sejam a geração de eventos.

PS: Nunca emito eventos do construtor do, Aggregatepois há uma distinção entre criar uma instância de um objeto (no sentido da linguagem de programação) e a criação (o nascimento, se você quiser) de um Aggregate. Emito eventos apenas de um handlemétodo (ao manipular a command).


Como assim AR1WasCreated? Deveria ser AR2WasCreated? Além disso, se eu usar sua lógica, emito um evento AR2WasCreatedantes 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).
Bojan Vukasovic 22/02

OK, 3 anos depois. Vai AR1WasCreated-> SAGA (tem regra se A1 foi criado, então crie A2) -> CreateAR2Command-> AR2WasCreated.
Bojan Vukasovic 19/01

@BojanVukasovic Estou feliz que funcionou como eu escrevi :)
Constantin Galbenu 23/01

2

Como devemos criar novas raízes agregadas na arquitetura cqrs?

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.

No caso de usar a fonte de eventos, o ShouldCreateAR2Event não seria preservado no armazenamento de eventos, pois não afeta o estado da primeira raiz agregada.

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.

Ou ainda devemos salvar isso na loja de eventos?

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.


obrigado pela resposta. Mais uma coisa que não está 100% clara - mais tarde, se eu tiver que usar o AR2 como argumento para o AR1, como devo passar isso - já que o CQRS afirma que o AR deve ser usado apenas para escrever e não consultar. Mas não tenho outra opção senão usar, 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).
Bojan Vukasovic

> "Sim, se você está apenas lançando um evento no barramento síncrono para executar o código remoto, não deve salvar esse evento no livro de registro." Acho que salvá-lo tem um valor real, pois você sabe que o processo foi iniciado para que algo aconteça, agora você também pode acompanhar se isso realmente foi concluído. Mas eu acho que depende do caso de uso
Chaosekie
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.