Origem do evento e REST


17

Me deparei com o design de Event Sourcing e gostaria de usá-lo em um aplicativo em que um cliente REST é necessário (RESTful para ser preciso). No entanto, não consigo conectá-los, pois o REST é bastante CRUD e a fonte de eventos é baseada em tarefas. Eu queria saber como você pode projetar a criação de comandos com base em solicitações ao servidor REST. Considere este exemplo:

Com o REST, você pode colocar um novo estado no recurso chamado Arquivo. Em uma solicitação, você pode enviar um novo nome de arquivo, alterar a pasta pai e / ou alterar o proprietário do arquivo e assim por diante.

Como construir o servidor para que eu possa usar a fonte de eventos. Eu estava pensando sobre estas possibilidades:

  1. Determinar no servidor que os campos foram alteradas e criar comandos apropriados ( RenameFileCommand, MoveFileCommand, ChangeOwnerCommand, ...) e despachar estes individualmente. No entanto, nessa configuração, cada um dos comandos pode falhar, deixando outros fora da transação e, portanto, fora da alteração "atômica" do recurso.

  2. Despacho apenas um comando ( UpdateFileCommand) e no manipulador de comando, mais precisamente, no total, determinar quais campos foram alterados e enviar eventos individuais em vez ( FileRenamedEvent, FileMovedEvent, OwnerChangedEvent, ...)

  3. Este não é o que eu mais gosto: na solicitação ao servidor, especificaria nos cabeçalhos que comando usar, porque a interface do usuário ainda é baseada em tarefas (mas a comunicação é feita via REST). No entanto, ele falhará em qualquer outro uso da comunicação REST (por exemplo, em aplicativos externos), pois eles não são obrigados a alterar apenas o campo único em uma solicitação. Também trago um grande acoplamento ao back-end baseado em UI, REST e ES.

Qual você prefere ou existe alguma maneira melhor de lidar com isso?

Nota lateral: aplicativo escrito em Java e Axon Framework para fornecimento de eventos.


Certamente não o terceiro. Eu faria o primeiro, mas tenho que pensar sobre isso.
Inf3rno

Você está questionando se uma única solicitação HTTP pode resultar em vários comandos? Eu entendo bem? Na minha humilde opinião. deve ser capaz de fazê-lo, mas não leio sobre DDD há algum tempo, portanto, tenho que verificar um código de exemplo sobre como implementar isso.
Inf3rno

Encontrei um exemplo, mas é CRUD. github.com/szjani/predaddy-issuetracker-sample/blob/3.0/src/hu/… Vou perguntar ao autor qual é a sua opinião, ele sabe mais sobre DDD do que eu.
Inf3rno

1
O meu é que você deve usar vários comandos em uma "unidade de trabalho" se desejar consistência imediata. Se você está falando sobre eventual consistência, a pergunta não faz sentido para mim. Outra solução possível para enviar um CompositeCommand que pode conter os comandos que você deseja executar atômicos. Pode ser uma coleção simples, a única coisa que importa é que o barramento possa lidar com isso adequadamente.
Inf3rno

1
Segundo ele, você deve tentar obter uma relação 1: 1 entre comandos e solicitações HTTP. Se você não pode fazê-lo (que é um mau cheiro), use um volume (eu o chamei de composto) para torná-lo atômico.
Inf3rno

Respostas:


11

Eu acho que você pode ter um processo de usuário para incompatibilidade de implementação aqui.

Primeiro: um usuário honestamente deseja executar várias alterações em um arquivo simultaneamente? Uma renomeação (que pode ou não incluir uma mudança de caminho?), Mudança de propriedade e talvez alteração do conteúdo do arquivo (por uma questão de argumento) parecem ações separadas.

Vamos considerar o caso em que a resposta é "sim" - seus usuários realmente desejam fazer essas alterações simultaneamente.

Nesse caso, eu recomendo fortemente contra qualquer implementação que envia vários eventos - RenameFileCommand, MoveFileCommand, ChangeOwnerCommand- para representar essa única intenção do usuário.

Por quê? Porque os eventos podem falhar. Talvez seja extremamente raro, mas seu usuário enviou uma operação que parecia atômica - se um dos eventos posteriores falhar, o estado do seu aplicativo agora é inválido.

Você também está convidando riscos de corrida em um recurso claramente compartilhado entre cada um dos manipuladores de eventos. Você precisará escrever "ChangeOwnerCommand" de forma que o nome e o caminho do arquivo não importem, pois podem estar desatualizados no momento em que o comando é recebido.

Ao implementar um sistema tranqüilo não orientado a eventos com arquivos em movimento e renomear, prefiro garantir consistência usando algo como um sistema eTag - verifique se a versão do recurso que está sendo editado é a versão que o usuário recuperou pela última vez e falha se foi modificado desde então. Mas se você estiver despachando vários comandos para esta operação de usuário único, precisará incrementar a versão do recurso após cada comando - para que você não tenha como saber que o recurso que o usuário está editando é realmente a mesma versão do recurso que foi lido pela última vez .

O que quero dizer com isso é - e se outra pessoa executar outra operação no arquivo quase ao mesmo tempo. Os 6 comandos podem ser empilhados em qualquer ordem. Se tivéssemos apenas 2 comandos atômicos, o comando anterior poderia ter sucesso e o comando posterior poderia falhar "o recurso foi modificado desde a última vez que foi recuperado". Mas não há proteção contra isso quando os comandos não são atômicos, portanto a consistência do sistema é violada.

Curiosamente, existe um movimento em direção a algo como a arquitetura baseada em eventos no REST, chamada "Descansar sem PUT", recomendada no radar da tecnologia Thoughtworks, janeiro de 2015 . Há um blog consideravelmente mais longo sobre o Rest without PUT aqui .

Essencialmente, a idéia é que POST, PUT, DELETE e GET sejam adequados para aplicativos pequenos, mas quando você precisar começar a supor que a colocação, a postagem e a exclusão podem ser interpretadas na outra extremidade, você introduz o acoplamento. (por exemplo, "quando EXCLUIR o recurso associado à minha conta bancária, a conta deve ser fechada") E a solução proposta é tratar o REST de uma maneira mais originada por evento. Permite POST a intenção do usuário como um único recurso de evento.

O outro caso é mais simples. Se seus usuários não quiserem executar todas essas operações simultaneamente, não permita. POST um evento para cada intenção do usuário. Agora você pode usar o etag versioning em seus recursos.

Quanto aos outros aplicativos que estão usando uma API muito diferente dos seus recursos. Isso cheira a problemas. Você pode construir uma fachada da API antiga em cima da API RESTful e apontá-las para a fachada? expor um serviço que executa várias atualizações em um arquivo em sequência através do servidor REST?

Se você não criar a interface RESTful na parte superior da solução antiga, nem construir uma fachada da interface antiga na parte superior da solução REST e tentar manter as duas APIs apontando para um recurso de dados compartilhado, ocorrerá grandes dores de cabeça.


Eu posso ver a incompatibilidade, no entanto, pelo REST, em princípio, é possível atualizar o estado de vários campos, colocando o novo estado no recurso (por alguma representação). É assim que o REST funciona por definição - transferência de estado representacional, o lado negativo é que a intenção do usuário é perdida no processo. Além disso, o REST é quase um padrão para API externa. Quero apenas criar o aplicativo para poder usar os dois. Remover PUT é como solução alternativa por causa do ES. Pelo que posso ver, um comando de atualização que emite vários eventos é possível, desde que a manipulação de comandos e eventos esteja em uma transação.
21415 redhead

Etag é algo que eu gostaria de fazer de qualquer maneira. Além disso, seu link para "postagem mais longa no blog" é igual ao primeiro link.
21415 redhead

Voltarei a isso depois de alguma consideração com mais pensamentos sobre PUT. Certamente, remover o PUT não é apenas "uma solução alternativa para o ES". Corrigi o link do blog.
perfeccionista

4

Acabei de ler o artigo a seguir, que incentiva a especificação dos nomes de comando na solicitação ao servidor no cabeçalho Content-Type (enquanto segue 5 níveis do tipo de mídia).

No artigo, eles mencionam que o estilo RPC é ruim para o REST e sugerem a extensão do Content-Type para especificar o nome do comando:

Uma abordagem comum é usar recursos no estilo RPC, por exemplo / api / InventoryItem / {id} / rename. Embora isso aparentemente elimine a necessidade de verbos arbitrários, é contra a apresentação orientada a recursos do REST. Precisamos lembrar que um recurso é um substantivo e o verbo HTTP é o verbo / ação e as mensagens auto-descritivas (um dos princípios do REST) ​​são o veículo para transmitir outros eixos de informação e intenção. De fato, o comando na carga útil da mensagem HTTP deve ser suficiente para expressar qualquer ação arbitrária. No entanto, confiar no corpo da mensagem tem problemas próprios, já que o corpo geralmente é entregue como um fluxo e amortece o corpo por inteiro antes de identificar a ação nem sempre é possível nem sábia.

PUT /api/InventoryItem/4454c398-2fbb-4215-b986-fb7b54b62ac5 HTTP/1.1
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Content-Type:application/json;domain-model=RenameInventoryItemCommand`

O artigo está aqui: http://www.infoq.com/articles/rest-api-on-cqrs

Você pode ler mais sobre 5 níveis de tipo de mídia aqui: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html


Embora eles estejam expondo os eventos de domínio à API REST, o que eu consideraria uma prática ruim, gosto da solução, pois ela não cria um novo "protocolo" apenas para o CQRS, seja enviando nomes de comandos no corpo ou extra cabeçalho e permanece fiel aos princípios RESTful.

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.