Estou trabalhando em um aplicativo de exemplo pequeno para aprender os conceitos de CQRS e fornecimento de eventos. Eu tenho um Basket
agregado e um Product
agregado que deve funcionar de forma independente.
Aqui estão alguns pseudo códigos para mostrar a implementação
Basket { BasketId; OrderLines; Address; }
// basket events
BasketCreated { BasketId; }
ItemAdded { BasketId; ProductId; Quantity }
AddItemSucceeded { BasketId; ProductId; Quantity }
AddItemRevoked { BasketId; ProductId; Quantity }
ItemRemoved { BasketId; ProductId; Quantity }
CheckedOut { BasketId; Address }
Product { ProductId; Name; Price; }
// product events
ProductReserved { ProductId; Quantity }
ProductReservationFailed { ProductId; Quantity }
ProductReservationCancelled { ProductId; Quantity; }
Os comandos são bem parecidos com os eventos, usando o nome imperativo e não o pretérito.
No momento, eles funcionam bem de forma independente. Emito um comando AddItem
e ele cria um ItemAdded
evento no Basket
agregado que faz o que é necessário com o estado do 'Basket'. Da mesma forma, para o produto, o comando e os eventos funcionam perfeitamente.
Agora eu gostaria de combinar isso em um processo que seria algo assim (em termos de comandos e eventos que acontecem):
O gerente de processo faria o seguinte:
on BasketCreated: CreateShoppingProcess
on ItemAdded: ReserveProduct
on ProductReserved: SucceedAddingItem // does nothing, but needs to be there so that the basket knows it can check out
on ProductReservationFailed: RevokeAddItem
on RemoveItem: CancelProductReservation
on Checkout: CreateOrder // create an order and so on...
As perguntas para as quais não consegui encontrar respostas definitivas são:
- Preciso persistir no gerenciador de processos? Parece que sim, mas não tenho certeza
- Se sim, preciso salvar os eventos para o gerenciador de processos. No entanto, os eventos que está ouvindo estão vinculados aos agregados. Eu adiciono o ID do processo a esses? Tenho eventos separados apenas para o gerente de processos? Como fazer isso e manter o mais seco possível
- Como sei para que cesta
ProductReserved
servem os eventos? Tudo bem ter umBasketId
também, ou essas informações estão vazando? - Como mantenho um relacionamento entre os eventos, como sei qual
ItemAdded
produziu qualProductReserved
evento? Eu transmito umEventId
? Isso parece estranho ... - Devo implementar o
Basket
como um gerenciador de processos em vez de um simples agregado?
Depois de mais algumas pesquisas, cheguei a isso: uma saga é algo que mantém seus próprios eventos e ouve eventos de fora. Basicamente, é um agregado que também pode reagir a eventos que acontecem fora do próprio mundinho.
Um Gerenciador de processos trabalha com os eventos externos e envia comandos. Sua história pode ser reconstruída a partir dos eventos que ocorreram nos agregados que compartilham um identificador comum como um ID de correlação.
fonte
Respostas:
Revise o que Rinat Abdullin escreveu sobre a evolução dos processos de negócios . Observe, em particular, sua recomendação para o desenvolvimento de um processo de negócios em um ambiente de mudanças rápidas - um gerente de processos é "apenas" um substituto automático para um ser humano olhando para uma tela.
Meu próprio modelo mental de gerente de processos é que é uma projeção de origem de eventos que você pode consultar para obter uma lista de comandos pendentes.
É um modelo de leitura. Você pode reconstruir o gerenciador de processos a partir do histórico de eventos sempre que precisar; ou você pode tratá-lo como um instantâneo que você atualiza.
Não - o gerente de processos é um gerente . Não faz nada útil por si só; em vez disso, informa aos agregados para fazer o trabalho (ou seja, fazer alterações no livro de registro).
Certo. Nota: na maioria dos domínios de compras "reais", você não insistiria em reservar inventário antes de processar um pedido; adiciona contenção desnecessária aos negócios. É mais provável que sua empresa queira aceitar o pedido e peça desculpas nos raros casos em que o pedido não pode ser atendido no tempo necessário.
As mensagens têm metadados - em particular, você pode incluir um causationIdentifier (para identificar quais comandos produziram quais eventos) e um authenticationIdentifier , para rastrear geralmente a conversa.
Por exemplo, o gerenciador de processos grava seu próprio ID como o correlacionado no comando; os eventos produzidos por uma cópia do ID de correlação do comando e seu gerente de processos se inscreve em todos os eventos que possuem seu próprio ID de correlação.
Minha recomendação é não. Mas Udi Dahan tem uma opinião diferente que você deve revisar; é que o CQRS só faz sentido se seus agregados forem sagas - o Udi usou a saga no local em que o gerente de processos se tornou a ortografia dominante.
Na verdade não? Os gerentes de processo estão preocupados principalmente com a orquestração, não com o estado do domínio. Uma instância de um processo terá "estado", na forma de uma história de todos os eventos que eles observaram - a coisa correta a ser feita em resposta ao evento Z depende se vimos ou não os eventos X e Y Portanto, pode ser necessário armazenar e carregar uma representação desse estado (que pode ser algo simples ou pode ser o histórico de eventos observados).
(Eu digo "não realmente" porque agregado é definido de maneira vaga o suficiente para não ser totalmente errado afirmar que a lista de eventos observados é um "agregado". As diferenças são mais semânticas que a implementação - carregamos o estado do processo e depois decidimos quais mensagens enviar para as partes do sistema responsável pelo estado do domínio . Há um pouco de aceno manual aqui.)
Não é bem assim - a administração do estado não é um fazedor, é um detentor de rastreadores. Nas circunstâncias em que o gerente do processo não deve emitir nenhum sinal, você fornece conexões inertes ao mundo. Em outras palavras,
dispatch(command)
é um no-op.fonte
O que você está procurando tem um padrão chamado "Saga", que é essencialmente um gerenciador de processos.
Os Saga também são perfeitos para processos de execução longa, porque podem manter o estado entre os comandos correlacionados.
Aqui está um ótimo artigo sobre Sagas
fonte